home *** CD-ROM | disk | FTP | other *** search
/ Brotikasten / BROTCD01.iso / texte / cbmhack5.txt < prev    next >
Text File  |  1995-08-20  |  215KB  |  6,100 lines

  1.                    @@@@@@@@
  2.              @@@@@@@@@@@@@@@@@@
  3.          @@@@@@            @@@@@@
  4.       @@@@@
  5.     @@@@@  @@@@  @@@@      @@      @@@@@   @@@@  @@@@  @@@@  @@@@  
  6. @@@@   @@@@@
  7.   @@@@@    @@    @@      @@@@    @@   @@   @@  @@@     @@    @@@@  @@   @@   @@
  8.  @@@@@    @@@@@@@@     @@  @@   @@        @@@@@       @@    @@ @@ @@   @@
  9. @@@@@    @@    @@    @@@@@@@@  @@   @@   @@  @@@     @@    @@  @@@@   @@   @@
  10. @@@@@  @@@@  @@@@  @@@@  @@@@  @@@@@   @@@@  @@@@  @@@@  @@@@  
  11. @@@@   @@@@@@
  12. @@@@@                                                                     @@
  13.  @@@@@@            @@@@@@             Issue #5
  14.    @@@@@@@@@@@@@@@@@@              March 7, 1993
  15.        @@@@@@@@
  16.  
  17. -----------------------------------------------------------------------------
  18. Editor's Notes:
  19. by Craig Taylor
  20.  
  21.   It seems that each issue of C= Hacking has always began with a "Sorry, It's
  22.   late but here it is message." - Well, this one will start out again like 
  23.   that - This issue was originally scheduled to be out the middle of January
  24.   but due to several delays in obtaining articles and my delaying trying to
  25.   debug the multi-tasking source code it's been held up until now. 
  26.   
  27.   My apologies to the authors who have had their articles into me on time -
  28.   school is coming first for me and having to do a lot of coding for several
  29.   classes was the major contributing factor to the delays. 
  30.  
  31.   Now, after the apologies are out of the way - Let's take a look at what has
  32.   happened since last time I wrote.
  33.  
  34.   - RUN magazine is no longer with us.
  35.  
  36.   As one of the last hold-outs I was expecting RUN magazine to keep on printing
  37.   until the Commodore 64/128's really did die out but apparently the publishers
  38.   decided it wouldn't be so. This leaves the Twin Cities magazine as the only
  39.   US magazine in publication for the Commodore (6502 based) computers that I am
  40.   aware of. Speaking of Twin Cities (not sure if he's combining the 64/128 or
  41.   just coming out with seperate Twin Cities magazines) does anybody know or
  42.   have any information on when the next issue will be out? Or has my 
  43.   lastest issue just not been sent out?
  44.  
  45.   As I was writing this I got the latest issue of Twin Cities which has
  46.   expanded to C=64 coverage also. The new issue looks very nice, about 53
  47.   pages of so of good decent material. I'd recommend get a subscription for
  48.   those of you who are looking to still hear about new Commodore products.
  49.  
  50.   I'd like to get people's reactions on the demise of RUN and what will
  51.   people will think will probably be the main source of information for C=
  52.   owners.  A lot of people reading this magazine are on the comp.sys.cbm
  53.   newsgroup but I'm wondering about individuals who do not have access to
  54.   such a newsgroup and do not have access to the internet. Let me know what
  55.   you think - hopefully through a friend w/ access to the internet. Sort of
  56.   a catch-22 I guess.
  57.  
  58.   - A Mail-Server has been setup to automate sending issue requests.
  59.  
  60.   The full details of how to use the Mail-Server is in a documentation file
  61.   contained within but this mail-server (whose source code is available for
  62.   anyone who wishes to see it written in VAX DCL code) also allows file
  63.   requests which will be uuencoded and sent to you. I am trying to have all of
  64.   the programs in each issue available via request as for some people it is
  65.   a minor pain trying to extract and compile the programs contained within.
  66.  
  67.   - I saw a note recently that the speed-up board work was still being done.
  68.  
  69.   Does anybody know anything further about this? I'm interested in this and
  70.   how it would be carried out / done but aside from an occasional post here
  71.   and there about it I actually hear very little. 
  72.  
  73.   - There is also work on an Ansi C compiler being done.
  74.  
  75.   Recently a group of people (about 9 currently) are working on a C compiler
  76.   for the C=64 and C=128 which will eventually support the full ANSI C
  77.   library. A large list of extensions have been proposed and the compiler
  78.   will probably be released as either shareware or possibly, public domain.
  79.  
  80.   Ack! - This magazine keeps growing. The last issue was approx.
  81.   somewhere around 3000 lines, this one is just a tad over 6000. I'm
  82.   sure that we're not suffering the quality just because of the
  83.   quantity. :-) Be sure to take a look at the previous back issues
  84.   available via the Mail-Server and don't be afraid to suggest comments
  85.   or suggestions. While usually the authors are too busy to take ideas
  86.   for new programs we always welcome to hear how useful you find certain
  87.   programs included herein etc.
  88.  
  89.   Also I am looking for articles on any type of software project, hardware
  90.   project or general theory articles that you would like to submit. Just
  91.   leave me a message via email at "duck@pembvax1.pembroke.edu". Note also
  92.   that I've just signed up for a GENIE account and can be reached there via
  93.   C.TAYLOR37 once my account is approved.
  94.  
  95. =============================================================================
  96.  
  97.   Please note that this issue and prior ones are available via anonymous
  98.   FTP from ccosun.caltech.edu under pub/rknop/hacking.mag in addition to the
  99.   mailserver which is documented in this issue.
  100.  
  101. =============================================================================
  102.   
  103.   NOTICE: Permission is granted to re-distribute this "net-magazine", in 
  104.   whole, freely for non-profit use. However, please contact individual
  105.   authors for permission to publish or re-distribute articles seperately.
  106.   A charge of no greater than 5 US. Dollars or equivlent may be charged for
  107.   library service / diskette costs for this "net-magazine." 
  108.  
  109. =============================================================================
  110. In this Issue:
  111.  
  112. Mail-Server Documentation
  113.  
  114. This articles describes how to access the mail-server for Commodore Hacking
  115. and includes a list of currently available files and back-issues.
  116.  
  117. Stretching Sprites
  118.  
  119. It's possible to expand sprites to more than twice their original size, but 
  120. there is no need to expand all of them equally. This article examins how to
  121. expand them 2,3, or more multiples of their original size.
  122.  
  123. Rob Hubbard's Music: Disassembled, Commented and Explained
  124.  
  125. This article written by Anthony McSweeney, presents the valuable source to
  126. Rob Hubbard's first music routine. This routine was used in Rob's first 20
  127. or 30 musics, including such classics as Thing on a Spring (Gremlin Graphics),
  128. Commando (Elite), Thrust (Firebird), International Karate (System 3), and
  129. Proteus (also known as Warhawk, by Firebird). 
  130.  
  131. ZPM3 and ZCCP Enhancements for CP/M Plus from Simeon Cran
  132.  
  133. Although all the articles to date in C= Hacking have focused on 6510/ 8502
  134. programming, there have been some interesting developments on the Z80 front.
  135. C128 CP/M users should be aware of the benefits of a new set of enhancements
  136. to the operating system that offers inreased speed and flexibility as well
  137. as new features. If that isn't enough, this package will also run ZCPR 3.3
  138. utilities and applications that won't run under standard CP/M Plus.
  139.  
  140. Multi-Tasking on the C=128 - Part 1
  141.  
  142. This article examines the rudiments of Multi-Tasking and also details the
  143. system calls in the Multi-Tasking package to be released in the next issue
  144. of C= Hacking.
  145.  
  146. LITTLE RED WRITER: MS-DOS file reader/writer for the C128 and 1571/81.
  147.  
  148. This article is an extension on Little Red Reader which was presented in the
  149. last issue and allows for reading and writing of MS-Dos diskettes from and to
  150. 1571/81 drives. 
  151.  
  152. =============================================================================
  153. Mail-Server Documentation
  154. by Craig Taylor (duck@pembvax1.pembroke.edu)
  155.  
  156. What is a mail-server?
  157.  
  158.    A mailserver is an automated job that will scan my mail file for messages
  159.    with a subject line of "MAILSERV" and will then automatically carry out
  160.    certain operations within the body of the mail message. This makes it easier
  161.    on me and you. Easier for me so that I don't have to deal with 50+ messages
  162.    each month asking for files to be sent out and also insures that your files
  163.    that you requested will be sent within 24 hours. In addition it allows 
  164.    files to be more easily sent and accessed in case you are not able to 
  165.    extract the source files from C= Hacking. 
  166.  
  167.    If you have FTP access please see the Editor's Notes at the start for
  168.    information on R. Knop's FTP site and how to access it as you may find
  169.    using that somewhat quicker to use.
  170.  
  171. How to use the mail-server / What it is.
  172.  
  173.    This mail-server is intended to help me keep track / more easily update my
  174.    mailing list of individuals who wish to sub-scribe or get back-issues of
  175.    C= Hacking mailed to them. 
  176.  
  177.    To use it simply send a message to "duck@pembvax1.pembroke.edu" (me) with a
  178.    subject line of "MAILSERV" and then with one of the following commands in the
  179.    body of the mail message:
  180.  
  181. Currently the following commands are supported:
  182.  
  183.   help              - sends current documentation f file list
  184.   send iss<number>. - sends issue # (1-4 currently). Remember the period!!
  185.   subscribe         - subscribe to the mailing list automatically
  186.  *subscribe catalog - subscribes to a list that will be sent out 
  187.                       everytime the catalog changes.
  188.   catalog           - show list of available source /uuencoded binaries
  189.   psend name        - send uuencoded binary.
  190.  
  191. Commands no longer supported:
  192.  
  193.   status            - returns the current commands (this list)
  194.                       (use the help file)
  195.  
  196. Please note that the mailserver is only run at 2:00 AM EST.
  197.  
  198. Catalog List - Last update February 27, 1993.
  199.  
  200.   iss1.                 - C= Hacking, Issue #1
  201.   iss2.                 - C= Hacking, Issue #2
  202.   iss3.                 - C= Hacking, Issue #3
  203.   iss4.                 - C= Hacking, Issue #4
  204.   iss5.                 - C= Hacking, Issue #5
  205.   contents.lis          - Content Listing of Issues #1-4
  206.   mailserv.012493       - VAX/DCL Mailserver .share file
  207.  
  208.  *invasion1.sfx         - Space Invasion Source (Starting with Issue 4)
  209.  *bmover.sfx            - Geos 128 Banking with Banks 2f3 (Issue #2)
  210.  *vdc-bg.sfx            - Use of 64K VDC RAM in Geos (Issue #3)
  211.  *c64.zip               - C64 Emulator for IBM
  212.  *lrr.sfx               - Little Red Reader (from C= Hacking #4)
  213.  
  214.   -- Temporary Files -> Or files that will be deleted as needed for space
  215.  
  216.  *zedv075.sfx           - Zed-128 Text Editor 
  217.  *ramdosii.sfx          - REU Dos for the C=128 Allows > 512k REU.
  218.  
  219.   NOTE: Files marked with "*" should be requested via PSEND - they will be
  220.         sent to you in uuencoded form. They may _not_ be requested via SEND.
  221.  
  222. =============================================================================
  223. The Demo Corner: Stretching Sprites
  224. by Pasi 'Albert' Ojala (po87553@cs.tut.fi)
  225.         Written: 16-May-91  Translation/Revision 01-Jun-92, Dec-92
  226.  
  227. (All timings are in PAL, principles will apply to NTSC too)
  228.  
  229. You might have heard that it is possible to expand sprites to more than
  230. twice their original size. Imagine a sprite scroller with 6-times expanded
  231. sprites. However, there is no need to expand all of them equally. Using
  232. this technique, it is possible to make easy sinus effects and constantly
  233. expanding and shrinking letters.
  234.  
  235. The VIC (video interface controller) may be fooled in many things. One of
  236. them is the vertical expansion of sprites. If you clear the expand flag and
  237. then set it back straight away, VIC will think it has only displayed the
  238. first one of the expanded lines. If we do the trick again, VIC will continue
  239. to display the same data again and again. But why does VIC behave like this ?
  240.  
  241.  
  242. _Logic gates will tell the truth_
  243.  
  244. It is not really a bug, but a feature. The hardware design to implement the
  245. vertical enlargement was just as simple as possible. Those, who do not care
  246. about hardware should skip this part... The whole y-enlargement is handled
  247. with five simple logical ports. Each sprite has an associated Set-Reset
  248. flip-flop to tell whether to jump to the next sprite line (add three bytes
  249. to the data counter) or not.
  250.  
  251. Let's call the state of the flip-flop Q and the inputs R (reset) and S (set).
  252. The function of a SR flip-flop is quite simple: if R is one, Q goes to zero,
  253. if S is one, Q goes to one. Otherwise the state of the flip-flop does not
  254. change. In this case the flip-flop is Set, if either the Y-enlargement bit
  255. is zero or the state of the flip-flop is zero at the end of a scan line. The
  256. flip-flop is reset, if both the state and the Y-enlargement are ones at the
  257. end of the line.
  258.  
  259. When you clear the bit in the vertical expansion register, the flip-flop will
  260. be set regardless of the electron beam position on the scan line. If you
  261. set the bit again before the end of the line, the flip-flop will be cleared
  262. and VIC will be displaying the same sprite line again. In other words, VIC
  263. will think that it is starting to display the second line of the expanded
  264. sprite row. This way any of the lines in any of the sprites may be stretched
  265. as wanted.
  266.  
  267.  .---- Current flipflop state (if one, enables add to sprite pointer)
  268.  |  .---- Y-expansion bit.
  269.  |  |  .--- End of line pulse (briefly one at end of line)
  270.  |  |  |  .--- Next state (What state will become under these conditions)
  271.  |  |  |  |
  272.  0  0  0  1
  273.  0  0  1  1
  274.  0  1  0  no change
  275.  0  1  1  1
  276.  1  0  0  1             Clear $D017 -> flip-flop is set
  277.  1  0  1  1
  278.  1  1  0  no change     Set $D017   -> flip-flop resets at the end of line
  279.  1  1  1  0
  280.  
  281. So, simply, at any time, if vertical expand is zero, the add enable is set
  282. to one. At the end of the line - before adding - the state is cleared if
  283. vertical expand is one.
  284.  
  285.  
  286. _Even odder ?_
  287.  
  288. Something very weird happens when we clear the expansion bit right when VIC
  289. is adding three to the sprite image counters. The values in the counters will
  290. be increased only by two, and the data is then read from the wrong place.
  291.  
  292. Normally the display of a sprite ends when VIC has shown all of the 21
  293. lines of the sprite (the counter will end up to $3f). If there has been a
  294. counter mixup, $3f is not reached after 21 lines and VIC will go on counting
  295. and will display the sprite again, now normally. If we fool the counter only
  296. once, the counter value $3f is reached when the sprite is displayed twice.
  297.  
  298.  
  299. _Fiddling_
  300.  
  301. I don't think the distorted counter effect can be used for anything, but
  302. there is many things where the variable stretching could be used. When you
  303. open the borders, you can be sure that there is a constant amount of time,
  304. if you stretch the sprites to the whole lenght of the area. You may stretch
  305. only the first and last lines, stretch the other lines by a constant or
  306. using a table, or using a variable table or any of the combinations possible.
  307.  
  308.  
  309. _A raster routine is a must_
  310.  
  311. Because you have to access the VIC registers on each line during the stretch,
  312. you need some kind of routine which can do other kinds of tricks besides the
  313. stretch. You can open the side borders and change the background color and
  314. maybe you have to shift the screen (and the bad lines with it) downwards.
  315. [See previous C=Hacking Issues for talk about raster interrupts.]
  316.  
  317. Look at the demo program. In the beginning of the raster routine there is
  318. first some timing, then a loop that lasts exactly 46 clock cycles. It takes
  319. exactly one scan line to execute. Inside the loop we first do the necassary
  320. modifications to the vertical scroll register, then we change the background
  321. color and then we open the side borders. And finally we handle the stretching
  322. using the stretch data, where a zero-bit means that the corresponding sprite
  323. will be stretched. A one-bit means that VIC is allowed to go to the next line
  324. of the sprite data.
  325.  
  326.  
  327. _Stretching takes time_
  328.  
  329. Besides showing the stretched sprites we need time to generate the stretching
  330. data, unless of course, the stretch is constant. We have to have 20
  331. one-bits for each sprite in our table. It is not feasible to determine the
  332. state of each byte in the table, instead you clear the table and plot the
  333. needed bits.
  334.  
  335. The routine is quite straightforward, but many optimizations may be applied
  336. to make it faster. First we load Y with the stretch of the first line (the
  337. y-coordinate of the data). Then we use it as an index to the table and plot
  338. the right bit and increase Y with the expansion value. Then we do it again
  339. until we have all of the 20 bits scattered to the table. The last sprite line
  340. will then stretch until we stop the stretching, because the last line is
  341. not allowed to be drawn.
  342.  
  343.  
  344. _Speed is everything_
  345.  
  346. The calculation itself is easy, but optimizing the routine is not. If all
  347. of the sprites are stretched equally (by integer amounts) and from the same
  348. position, the routine is the fastest possible.  You can also have variable
  349. and smooth stretch.  Smooth stretch uses other than integer expansion values
  350. and thus also needs more processor time.  If each sprite has to be stretched
  351. individually, you need much more time to do it.
  352.  
  353. The fastest routine I have ever written uses some serious selfmodification
  354. tricks. There are also some other tricks to speed up the stretch, but they
  355. are all secret ones.. :-)  Well, what the h*ck, I will include it anyway.
  356. By the time you read this I have already made a faster routine..
  357.  
  358. You can speed up that routine (by 17%) by unrolling the inner loop, but you
  359. have to use a different addressing mode for ORA (zero-page). You also need
  360. to place some restrictions to the tables used.. If you unroll both loops,
  361. you can get ~25% faster routine than the Fore!-version.
  362.  
  363.  
  364. _Demo program_
  365.  
  366. I tried to collect all of the main principles of stretching and raster
  367. routines to the demo program. I use the term "raster routine" when the
  368. execution is tightly synchronized to the electron beam and to the screen
  369. display. The program may be unclear in places, but I wanted to keep it as
  370. short as possible. The routine opens the side borders, scrolls the screen
  371. vertically, changes the background color and stretches the sprites.
  372.  
  373. The stretcher routine allows different y-position and amount of expansion
  374. for each sprite. This routine uses 1/8 fractions to do the counting, and so
  375. it is much too slow to use in a real demo.  VIC registers are initialized
  376. from a table, instead of setting them separately. Interrupt position is one
  377. line above the sprites. The program does not open the top or bottom borders.
  378. (I usually use a NMI to open the vertical borders, so that I only need to
  379.  use one raster-IRQ position.)
  380.  
  381. I tried to make a NTSC version, but I couldn't get it to synchronize.
  382. There are also less cycles available so you can't stretch all of the sprites
  383. individually in NTSC (with this routine that is..).
  384.  
  385. --------------------------------------------------------------------------
  386. Fast-stretch from Megademo92 (part: Fore!)
  387.  
  388. SINPOS          Stretch sinus index
  389. SINSPEED        Stretch sinus index speed
  390. YSINPOS         Y-sinus index
  391. YSINSPEED       Y-sinus index speed
  392. MASK            Bit mask for passess (usually $01,$02,$04,$08,$10..)
  393.  
  394. YSINUS          Y-sinus table
  395. STRETCH         Sprite line sizes   (LSB of the address must be 0)
  396. SIZET           Sprite size/2 table (LSB of the address must be 0)
  397. DATA            Stretch data table (cleared before this routine)
  398.  
  399. [xx] marks selfmodification. For example loop counter, bit mask and
  400. index to the stretch and size data tables are stored straight in the
  401. code.
  402.  
  403. 0b90    lda #$06        ; Number of sprites-1 (here I used only 7 sprites)
  404. 0b92    sta $0b96
  405. 0b95    ldx #$[ff]      ; Load counter
  406. 0b97    clc             ; Clear carry for adc
  407. 0b98    lda SINPOS,x    ; Stretch sinus position
  408. 0b9b    sta $0bd1       ; Set low bytes of indices
  409. 0b9e    sta $0bb8
  410. 0ba1    adc SINSPEED,x  ; Add stretch sinus speed (carry is not set)
  411. 0ba4    and #$7f        ; Table is 128 bytes (twice)
  412. 0ba6    sta SINPOS,x    ; Save new sinus position
  413. 0ba9    lda YSINPOS,x   ; Get the Y sinus position
  414. 0bac    adc YSINSPEED,x ; Add Y sinus speed
  415. 0baf    sta YSINPOS,x   ; Save new Y sinus position
  416. 0bb2    tay             ; Position to index register
  417. 0bb3    lda YSINUS,y    ; Get Y-position from table (can be 256 bytes long)
  418. 0bb6    sec             ; adc either sets or clears carry, we have to set it
  419. 0bb7    sbc SIZET[1e]   ; Subtract size of the sprite/2 to get the sprite
  420. 0bba    clc             ;  to stretch from the middle.
  421. 0bbb    tay             ; MaxSize/2 < Y-sinus < AreaHeight-MaxSize/2
  422. 0bbc    lda MASK,x      ; Get the ora-mask for this pass
  423. 0bbf    sta $0bcb       ; Store mask
  424. 0bc2    sta $0bdb
  425. 0bc5    ldx #$13        ; 19 lines here + 1 after
  426. 0bc7    lda DATA,y      ; Load & ora-mask & store
  427. 0bca    ora #[$01]
  428. 0bcc    sta DATA,y
  429. 0bcf    tya
  430. 0bd0    adc STRETCH[1e],x ; Add the stretch from the table (carry is not set)
  431. 0bd3    tay
  432. 0bd4    dex             ; decrease counter
  433. 0bd5    bne $0bc7       ; Do the 19 lines
  434. 0bd7    lda DATA,y      ; Load & ora-mask & store the 20th line
  435. 0bda    ora #[$01]
  436. 0bdc    sta DATA,y
  437. 0bdf    dec $0b96       ; Next sprite(s)
  438. 0be2    bpl $0b95
  439. 0be4    rts
  440.  
  441. Timings:
  442. -------
  443. clear 128 bytes: 514  + 12 cycles       8.16 lines
  444. 7 passes       : 3820 + 12 cycles       60.6 lines = 8.66 lines/pass
  445.  
  446. The unrolled clear routine consists of one load (lda #$00) and 128
  447. store instructions (sta $nnnn). 12 cycles are counted for JSR/RTS.
  448.  
  449. Stretching of 8 sprites would take slightly less than 80 lines, which is one
  450. fourth of the total raster time. Displaying a 128-line high stretcher takes
  451. about 130 lines (counting sprite setup and synchronization), scroller couple
  452. of lines more. Total 212 lines leaves 100 lines (6300 cycles) free for other
  453. activities in a PAL system. In a NTSC system you would have only 50 lines
  454. left.
  455.  
  456.  
  457. A simple basic routine to create the stretch data:
  458. -------------------------------------------------
  459. a=0:for f=0 to 127:a=a+Height*(2+sin(f*PI/64)):poke Table+f,a:
  460. poke Table+f+128,a:a=a-int(a):next f
  461.  
  462. This will also handle the 'rounding'. Because of this we don't have to
  463. handle fractions in the stretcher routine. The use of a table also gives the
  464. opportunity to have a separate size for each sprite line. The table does
  465. not need to be a sinus, it could have triangle or any other 'waveform' as
  466. long as the minimum value in the table (sprite line size) is 1.
  467.  
  468.  
  469. A basic routine to do the size/2 table:
  470. --------------------------------------
  471. a=0:for f=0 to 19:a=a+peek(Table+f):next f: rem get the size in position 0
  472. for f=0 to 127:poke STable+f,a/2:a=a-peek(Table+f)+peek(Table+f+20):next f
  473.  
  474. --------------------------------------------------------------------------
  475. _Stretcher program_
  476.  
  477. YSCROLL= $CF00 ; Vertical scroll table (moves bad lines)
  478. STRETCH= $CF80 ; Stretch table
  479. COLORS=  $CE80 ; Table for background colors
  480. YCOORD=  $0380 ; Sprite y-positions (eight bytes)
  481. HEIGHT=  $0388 ; Sprite stretches   (eight bytes)
  482. YPOS=    52    ; Sprite y-coordinate
  483. SPRCOL=  2     ; Sprite colors
  484.  
  485.  
  486. *= $C000
  487.  
  488.         SEI             ; Disable interrupts
  489.         LDA #$7F
  490.         STA $DC0D       ; Disable timer interrupts
  491.         LDA #<IRQ       ; Our own interrupt handler
  492.         STA $0314
  493.         LDA #>IRQ
  494.         STA $0315
  495.         LDX #$3E        ; We create a sprite to cassette buffer
  496. LOOP    LDA SPRITE,X
  497.         STA $0340,X
  498.         DEX
  499.         BPL LOOP
  500.         LDX #7
  501. LOOP2   LDA #$D         ; Set the sprite image pointers
  502.         STA $07F8,X
  503.         LDA #SPRCOL     ; Set sprite colors
  504.         STA $D027,X
  505.         DEX
  506.         BPL LOOP2
  507.         LDX #$26
  508. LOOP3   LDA VIDEO,X     ; Init VIC
  509.         STA $D000,X
  510.         DEX
  511.         BPL LOOP3
  512.         LDX #$7F        ; Create the y-scroll table
  513. LOOP4   TXA             ;  and clear the color table
  514.         AND #$07
  515.         ORA #$10        ; Non-blank screen
  516.         STA YSCROLL,X
  517.         LDA #$00
  518.         STA COLORS,X
  519.         DEX
  520.         BPL LOOP4
  521.         STA $3FFF
  522.         LDX #23         ; Create a color table
  523. LOOP5   LDA BACK,X
  524.         STA COLORS+8,X
  525.         STA COLORS+32,X
  526.         STA COLORS+56,X
  527.         STA COLORS+80,X
  528.         STA COLORS+96,X
  529.         DEX
  530.         BPL LOOP5
  531.         JSR CHANGE      ; Init sprite sizes and y-positions
  532.         CLI             ; Enable interrupts
  533.         RTS
  534.  
  535. IRQ     LDX #$01
  536.         LDY #$08        ; 'normal' $D016
  537.         NOP             ; Timing
  538.         NOP
  539.         NOP
  540.         BIT $EA         ; (Add NOP's etc. for NTSC)
  541. LOOP6   LDA YSCROLL-1,X ; Move the screen (bad lines)      5
  542.         STA $D011                                          4
  543.         LDA COLORS,X    ; Load the background color        4
  544.         DEC $D016       ; Open the border                  6
  545.         STA $D021       ; Set the background color         4
  546.         STY $D016       ; Screen to normal                 4
  547.         LDA STRETCH,X   ; Stretch the sprites              4
  548.         STA $D017                                          4
  549.         EOR #$FF                                           2
  550.         STA $D017                                          4
  551.                         ; (Add NOP for NTSC     +2)
  552.         INX             ; Increase counter                 2
  553.         BPL LOOP6       ; Loop 127 times                 + 3
  554.                                                          ---
  555.         LDA #1          ; Ack the raster interrupt       =46
  556.         STA $D019                                        +17(sprites)
  557.                                                          ---
  558.         JSR DOSTRETCH   ; New stretch                    =63(whole)
  559.  
  560.         JMP $EA31
  561.  
  562. SPRITE  BYT 0,0,0,3,$FB,0,7,$7E          ; An Example sprite
  563.         BYT 0,$35,$DF,0,$1D,$77,0,$B7
  564.         BYT $5D,0,$BD,$83,$7E,$EF,1,$DE
  565.         BYT $BB,1,$78,$AE,3,$70,$EB,0
  566.         BYT 0,$BA,3,$60,$EE,3,$D8,$FB
  567.         BYT 2,$F6,$FE,$83,$BD,$9F,$BA,0
  568.         BYT $37,$EE,0,$3D,$FB,0,7,$7E
  569.         BYT 0,3,$DF,0,0,0,0
  570.  
  571. VIDEO   BYT $E8,YPOS,$20,YPOS,$50,YPOS,$80,YPOS,$B0,YPOS
  572.         BYT $E0,YPOS,$10.YPOS,$40,YPOS,$C1,$18,YPOS-1,0,0
  573.         BYT $FF,8,$FF,$15,1,1,$FF,$FF,$FF,0,0,0,0,0,0,0,1,10
  574.         ; Init values for VIC - sprites, interrupts, colors
  575.  
  576. BACK    BYT 0,$B,$C,$F,1,$F,$C,$B   ; Example color bars
  577.         BYT 0,6,$E,$D,1,$D,$E,6
  578.         BYT 0,9,2,$A,1,$A,2,9
  579.  
  580. DOSTRETCH
  581.         LDX #31            ; Clear the table
  582.         LDA #0             ; (Unrolling will help the speed,
  583. LOOP7   STA STRETCH,X      ;  because STA nnnn,X is 5 cycles
  584.         STA STRETCH+32,X   ;  and STA nnnx is only 4 cycles.)
  585.         STA STRETCH+64,X
  586.         STA STRETCH+96,X
  587.         DEX
  588.         BPL LOOP7
  589.         STA REMAIND+1      ; Clear the remainder
  590.         LDA #7
  591.         STA COUNTER+1      ; Init counter for 8 loops
  592.         LDA #$80
  593.         STA MASK+1         ; First sprite 7, mask is $80
  594. COUNTER LDX #$00           ; The argument is the counter
  595.         LDY YCOORD,X       ; y-position
  596.         LDA HEIGHT,X       ; Height of one line (5 bit integer part)
  597.         STA ADD+1
  598.         LDX #20            ; Handle 20 lines
  599. LOOP8   LDA STRETCH+2,Y
  600. MASK    ORA #$00
  601.         STA STRETCH+2,Y    ; Set a one-bit
  602.         STY YADD+1
  603. REMAIND LDA #0
  604.         AND #7             ; Previous remainder
  605. ADD     ADC #0             ;  add to the height
  606.         STA REMAIND+1      ; Save the new value
  607.         LSR
  608.         LSR
  609.         LSR
  610.         CLC                ; Take the integer part
  611. YADD    ADC #0
  612.         TAY                ; New value to y-register
  613.         DEX
  614.         BNE LOOP8
  615.         LSR MASK+1         ; Use new mask
  616.         DEC COUNTER+1      ; Next sprite
  617.         BPL COUNTER
  618.  
  619. CHANGE  LDA #$00
  620.         ASL                ; Sprite height changes with 2x speed
  621.         AND #$3F
  622.         TAY                ; 64 bytes long table
  623.         INC CHANGE+1       ; Increase the counter
  624.         LDX #7             ; Do eight sprites
  625. LOOP9   LDA SINUS,Y
  626.         LSR
  627.         LSR
  628.         CLC                ; Use the same sinus as y-data
  629.         ADC #8
  630.         STA HEIGHT,X       ; Sprite height will be from 1 to 3 lines
  631.         TYA
  632.         ADC #10            ; Next sprite enlargement will be 10 entries
  633.         AND #$3F           ;  from this
  634.         TAY
  635.         DEX
  636.         BPL LOOP9
  637.         LDX #7
  638.         LDA CHANGE+1
  639.         AND #$3F
  640.         TAY
  641. LOOP10  LDA SINUS,Y        ; Y-position
  642.         STA YCOORD,X
  643.         TYA
  644.         ADC #10            ; Next sprite position is 10 entries from this one
  645.         AND #$3F
  646.         TAY
  647.         DEX
  648.         BPL LOOP10
  649.         RTS
  650.  
  651. SINUS   BYT $20,$23,$26,$29,$2C,$2F,$31,$34 ; A part of a sinus table
  652.         BYT $36,$38,$3A,$3C,$3D,$3E,$3F,$3F
  653.         BYT $3F,$3F,$3F,$3E,$3D,$3C,$3A,$38
  654.         BYT $36,$34,$31,$2F,$2C,$29,$26,$23
  655.         BYT $20,$1C,$19,$16,$13,$10,$E,$B
  656.         BYT 9,7,5,3,2,1,0,0,0,0,0,1,2,3,5,7
  657.         BYT 9,$B,$E,$10,$13,$16,$19,$1C
  658.  
  659. --------------------------------------------------------------------------
  660. Stretching sprites demo program basic loader (PAL)
  661.  
  662. 1 S=49152
  663. 2 DEFFNH(C)=C-48+7*(C>64)
  664. 3 CH=0:READA$,A:PRINTA$:IFA$="END"THENPRINT"<clr>":SYS49152:END
  665. 4 FORF=0TO31:Q=FNH(ASC(MID$(A$,F*2+1)))*16+FNH(ASC(MID$(A$,F*2+2)))
  666. 5 CH=CH+Q:POKES,Q:S=S+1:NEXT:IFCH=ATHEN3
  667. 6 PRINT"CHECKSUM ERROR":END
  668. 100 DATA 78A9648D1403A9C08D1503A23EBD96C09D4003CA10F7A207A90D9DF807A9029D, 
  669. 3614
  670. 101 DATA 27D0CA10F3A226BDD5C09D00D0CA10F7A27F8E0DDC8A290709109D00CFA9009D, 
  671. 3897
  672. 102 DATA 80CECA10F08DFF3FA217BDFCC09D88CE9DA0CE9DB8CE9DD0CE9DE0CECA10EB20, 
  673. 5281
  674. 103 DATA 67C15860A201A008EAEAEA24EABDFFCE8D11D0BD80CECE16D08D21D08C16D0BD, 
  675. 4699
  676. 104 DATA 80CF8D17D049FF8D17D0E810E0EE19D02014C14C31EA00000003FB00077E0035, 3394
  677. 105 DATA DF001D7700B75D00BD837EEF01DEBB0178AE0370EB0000BA0360EE03D8FB02F6, 
  678. 3628
  679. 106 DATA FE83BD9FBA0037EE003DFB00077E0003DF00000000E834203450348034B034E0, 3015
  680. 107 DATA 3410344034C118330000FF08FF150101FFFFFF00000000000000010A000B0C0F, 1859
  681. 108 DATA 010F0C0B00060E0D010D0E060009020A010A0209A21FA9009D80CF9DA0CF9DC0, 1876
  682. 109 DATA CF9DE0CFCA10F18D4DC1A9078D35C1A9808D45C1A200BC8003BD88038D51C1A2, 4314
  683. 110 DATA 14B982CF09009982CF8C5AC1A900290769008D4DC14A4A4A186900A8CAD0E24E, 3430
  684. 111 DATA 45C1CE35C110CDA9000A293FA8EE68C1A207B99EC14A4A1869089D880398690A, 3474
  685. 112 DATA 293FA8CA10ECA207AD68C1293FA8B99EC19D800398690A293FA8CA10F1602023, 3622
  686. 113 DATA 26292C2F313436383A3C3D3E3F3F3F3F3F3E3D3C3A383634312F2C292623201C, 1654
  687. 114 DATA 191613100E0B09070503020100000000000102030507090B0E101316191C0000, 296
  688. 200 DATA END,0
  689.  
  690. --------------------------------------------------------------------------
  691. Uuencoded C64 exutable for stretching sprites (PAL)
  692.  
  693. begin 644 stretch.64
  694. M`0@-"`$`4[(T.3$U,@`F"`(`EJ5(*$,ILD.K-#BJ-ZPH0[$V-"D`40@#`$-(?
  695. MLC`ZAT$D+$$ZF4$D.HM!)+(B14Y$(J>9(I,B.IXT.3$U,CJ``(@(!`"!1K(P/
  696. MI#,Q.E&RI4@HQBC**$$D+$:L,JHQ*2DIK#$VJJ5(*,8HRBA!)"Q&K#*J,BDI:
  697. M*0"I"`4`0TBR0TBJ43J74RQ1.E.R4ZHQ.H(ZBT-(LD&G,P#!"`8`F2)#2$5#F
  698. M2U-532!%4E)/4B(Z@``."60`@R`W.$$Y-C0X1#$T,#-!.4,P.$0Q-3`S03(S3
  699. M14)$.39#,#E$-#`P,T-!,3!&-T$R,#=!.3!$.41&.#`W03DP,CE$+"`S-C$TP
  700. M`%L)90"#(#(W1#!#03$P1C-!,C(V0D1$-4,P.40P,$0P0T$Q,$8W03(W1CA%4
  701. M,$1$0SA!,CDP-S`Y,3`Y1#`P0T9!.3`P.40L(#,X.3<`J`EF`(,@.#!#14-!B
  702. M,3!&,#A$1D8S1D$R,3="1$9#0S`Y1#@X0T4Y1$$P0T4Y1$(X0T4Y1$0P0T4Y1
  703. M1$4P0T5#03$P14(R,"P@-3(X,0#U"6<`@R`V-T,Q-3@V,$$R,#%!,#`X14%%?
  704. M045!,C1%04)$1D9#13A$,3%$,$)$.#!#14-%,39$,#A$,C%$,#A#,39$,$)$G
  705. M+"`T-CDY`$(*:`"#(#@P0T8X1#$W1#`T.49&.$0Q-T0P13@Q,$4P144Q.40P4
  706. M,C`Q-$,Q-$,S,45!,#`P,#`P,#-&0C`P,#<W13`P,S4L(#,S.30`CPII`(,@V
  707. M1$8P,#%$-S<P,$(W-40P,$)$.#,W145&,#%$14)",#$W.$%%,#,W,$5",#`P<
  708. M,$)!,#,V,$5%,#-$.$9",#)&-BP@,S8R.`#<"FH`@R!&13@S0D0Y1D)!,#`SN
  709. M-T5%,#`S1$9",#`P-S=%,#`P,T1&,#`P,#`P,#!%.#,T,C`S-#4P,S0X,#,TX
  710. M0C`S-$4P+"`S,#$U`"D+:P"#(#,T,3`S-#0P,S1#,3$X,S,P,#`P1D8P.$9&B
  711. M,34P,3`Q1D9&1D9&,#`P,#`P,#`P,#`P,#`P,3!!,#`P0C!#,$8L(#$X-3D`<
  712. M=@ML`(,@,#$P1C!#,$(P,#`V,$4P1#`Q,$0P13`V,#`P.3`R,$$P,3!!,#(PK
  713. M.4$R,49!.3`P.40X,$-&.41!,$-&.41#,"P@,3@W-@##"VT`@R!#1CE$13!#0
  714. M1D-!,3!&,3A$-$1#,4$Y,#<X1#,U0S%!.3@P.$0T-4,Q03(P,$)#.#`P,T)$G
  715. M.#@P,SA$-3%#,4$R+"`T,S$T`!`,;@"#(#$T0CDX,D-&,#DP,#DY.#)#1CA#=
  716. M-4%#,4$Y,#`R.3`W-CDP,#A$-$1#,31!-$$T03$X-CDP,$$X0T%$,$4R-$4LQ
  717. M(#,T,S``70QO`(,@-#5#,4-%,S5#,3$P0T1!.3`P,$$R.3-&03A%138X0S%!C
  718. M,C`W0CDY14,Q-$$T03$X-CDP.#E$.#@P,SDX-CDP02P@,S0W-`"J#'``@R`RJ
  719. M.3-&03A#03$P14-!,C`W040V.$,Q,CDS1D$X0CDY14,Q.40X,#`S.3@V.3!!\
  720. M,CDS1D$X0T$Q,$8Q-C`R,#(S+"`S-C(R`/<,<0"#(#(V,CDR0S)&,S$S-#,V*
  721. M,S@S03-#,T0S13-&,T8S1C-&,T8S13-$,T,S03,X,S8S-#,Q,D8R0S(Y,C8R+
  722. M,S(P,4,L(#$V-30`0PUR`(,@,3DQ-C$S,3`P13!",#DP-S`U,#,P,C`Q,#`PR
  723. M,#`P,#`P,#`Q,#(P,S`U,#<P.3!",$4Q,#$S,38Q.3%#,#`P,"P@,CDV`$\-E
  724. ,R`"#($5.1"PP````>
  725. ``
  726. end
  727. size 1362
  728.  
  729. =============================================================================
  730. Rob Hubbard's Music: Disassembled, Commented and Explained
  731. by Anthony McSweeney (u882859@postoffice.utas.edu.au)
  732.  
  733. [Ed's Note: I questioned this article concerning copyright problems and he
  734. has assured me that it is legal to present it in entirity like this as it is
  735. past a certain # of years. Accordingly I'm presenting it and any concerns
  736. should be taken up with him and not myself.]
  737.  
  738. Introduction:
  739. ************
  740.  
  741.   How do you introduce someone like Rob Hubbard?? He came, he saw and he
  742. conquered the '64 world. In my estimation, this one man was resposible for
  743. selling more '64 software than any other single person. Hell! I think that Rob
  744. Hubbard was responsible for selling more COMMODORE 64's than any other person!
  745. I certainly bought my '64 after being blown away by the Monty on the Run music
  746. in December 1985. In the next few years, Rob would totally dominate the '64
  747. music scene, releasing one hit after another. I will even say that some really
  748. terrible games sold well only on the strength of their brilliant Rob Hubbard
  749. music (eg. KnuckleBusters and W.A.R.).
  750.  
  751.   So how did Rob achieve this success? Firstly (of course) he is a superb
  752. composer and musician, able to make the tunes that bring joy to our hearts
  753. everytime we hear them! (also consider the amazing diversity of styles of
  754. music that Rob composed). Secondly, he was able to make music which was suited
  755. to the strengths and limitations of the SID chip. Just recall the soundfx used
  756. at the beginning of Thrust, or in the Delta in-game music. Perhaps the biggest
  757. limitation of SID must be the meagre 3 channels that can be used, but most
  758. Hubbard songs appear to have four, five or even more instruments going (just
  759. listen to the beginning of Phantoms of the Asteriods for example... that's
  760. only one channel used!!). I could really go on for (p)ages identifying the
  761. outstanding things that Rob Hubbard did, so I will finally mention that Rob's
  762. coding skills and his music routines were a major factor in his success.
  763.  
  764.  
  765. The First Rob Hubbard Routine:
  766. *****************************
  767.  
  768.   Rob Hubbard created a superb music routine from the very first tune which
  769. was released (Confuzion). Furthermore, Rob used this routine to make music
  770. for a very long time, only changing it _slightly_ over time. The sourcecode
  771. that I present here was used (with slight modifications) in: Confuzion, Thing
  772. on a Spring, Monty on the Run, Action Biker, Crazy Comets, Commando, Hunter
  773. Patrol, Chrimera, The Last V8, Battle of Britain, Human Race, Zoids, Rasputin,
  774. Master of Magic, One Man & His Droid, Game Killer, Gerry the Germ, Geoff Capes
  775. Strongman Challenge, Phantoms of the Asteroids, Kentilla, Thrust,
  776. International Karate, Spellbound, Bump Set and Spike, Formula 1 Simulator,
  777. Video Poker, Warhawk or Proteus and many, many more! All you would need to do
  778. to play a different music is to change the music data at the bottom, and a few
  779. lines of the code.
  780.  
  781.   This particular routine has been ripped off by many famous groups and
  782. people over the years, but I don't think that they were ever generous enough
  783. to share it around. Can you remember The Judges and Red Software?? They made
  784. the famous Red-Hubbard demo, and used it in Rhaa-Lovely and many of their
  785. other productions. I'm sure that the (Atari) ST freaks reading this will love
  786. Mad Max (aka Jochen Hippel), and remember the BIG demo which featured approx
  787. 100 Rob Hubbard tunes converted to the ST. Although I hate to admit it, I
  788. decided to start sharing around my own sourcecode after receiving the amazing
  789. Protracker sourcecode (340K!) on the Amiga (thanks Lars Hamre). That made me
  790. shameful to be selfish, especially after I learned alot of from it. Why don't
  791. YOU share around your old sourcecodes too!
  792.  
  793.   The particular routine that is included below was ripped from Monty on the
  794. Run, and it appeared in memory from $8000 to about $9554. The complete
  795. routine had code for soundfx in it, which I have taken out for the sake of
  796. clarity. Although the routine is really tiny - a mere 900 or 1000 bytes of
  797. code, there are some amazingly complex concepts in it which require alot of
  798. explanation if you don't know much about computer music or SID. Fortunately
  799. for you, I have put in excellent label names for you, and also alot of really
  800. helpful and amazing comments. In fact, I think this sourcecode must have a
  801. much better structure and comments than Rob Hubbard's original!!! I think that
  802. the best way to understand the sourcecode is to study it, and figure out what
  803. is going on using the comments.
  804.  
  805.   In addition to the comments in the source, there are *3* descriptions of
  806. the routine in this article. The first tells you how to use the music routine
  807. when it's viewed as an already assembled 'module'. The second goes through an
  808. overview of the music and instrument data format, and is great for getting an
  809. overall feel for what the code is doing. The third description looks at the
  810. various sections of the code, and how they come together.
  811.  
  812.  
  813. How to use the sourcecode:
  814. *************************
  815.  
  816.     jsr    music+0 to initialize the music number in the accumulator
  817.     jsr    music+3 to play the music
  818.     jsr    music+6 to stop the music and quieten SID
  819.  
  820.   The music is supposed to run at 50Hz, or 50 times per second. Therefore
  821. PAL users can run the music routine off the IRQ like this:
  822.  
  823.     lda    #$00     ; init music number 0
  824.     jsr    music+0
  825.     sei             ; install the irq and a raster compare
  826.     lda    #<irq
  827.     ldx    #>irq
  828.     sta    $314
  829.     stx    $315
  830.     lda    #$1b
  831.     sta    $d011
  832.     lda    #$01
  833.     sta    $d01a
  834.     lda    #$7f
  835.     sta    $dc0d
  836.     cli
  837. loop =*
  838.     jmp    loop     ; endless loop (music is now playing off interrupt :-)
  839.  
  840. irq =*
  841.     lda    #$01
  842.     sta    $d019
  843.     lda    #$3c
  844.     sta    $d012
  845.  
  846.     inc    $d020    ; play music, and show a raster for the time it takes
  847.     jsr    music+3
  848.     dec    $d020
  849.  
  850.     lda    #$14
  851.     sta    $d018
  852.     jmp    $ea31
  853.  
  854.   If this method is used on NTSC machines, then the music will be running at
  855. 60Hz and will sound much to fast - possibly it might sound terrible. I'm
  856. afraid you'll have to put up with this unless YOU are good enough to make a
  857. CIA interrupt at 50Hz. As I havn't had to worry about NTSC users before,
  858. perhaps someone will send me the best way to do this...
  859.  
  860. [Ed. Note: You could also keep a counter for the IRQ and don't execute it
  861. every 6 interrupt. This will make it the right speed although the best
  862. solution is for modifying the CIA to 50Hz like he mentions above.]
  863.  
  864. How the music data is arranged:
  865. ******************************
  866.  
  867. 1. The music 'module' contains one or more songs.
  868.  
  869.   Each RH music is made up of a 'bunch' of songs in a single module. Thus
  870. the 'module' can have the title music, in-game music, and the game-over music
  871. all using the same playroutine (and even the same instruments :). The source
  872. that appears below only has the one song in it, and the music number is
  873. automatically set to 0 as a result (line 20). The label 'songs' is where you
  874. want to look for the pointers to the songs if you want to change this.
  875.  
  876. 2. Each song is made up of three tracks.
  877.  
  878.   We all know that there are only 3 channels on the SID chip, so there are
  879. also 3 tracks - one for each channel. When I said 'pointers to the songs'
  880. above, I was therefore referring to 'pointers to the three tracks that make up
  881. the song'...hence we are looking at the label 'songs' again. Each track needs
  882. a high and low pointer, so there are 6 bytes needed to point to a song.
  883.  
  884. 3. Each track is made up of a list of pattern numbers
  885.  
  886.   Each track consists of a list of the pattern numbers in the order in which
  887. they are to be played. Here we are looking at the labels 'montymaintr1' and
  888. 'montymaintr2'and 'montymaintr3'. Therefore I can tell you that the initial
  889. patterns played in this song are $11, $12 and $13 on channels 1,2 and 3
  890. respectively. The track is either ended with a $ff or $fe byte. A $ff means
  891. that the song needs to be looped when the end of the track is reached (like
  892. the monty main tune), while a $fe means that the song is only to be played
  893. once. The current offset into the track is often called the current POSITION
  894. for that track.
  895.  
  896. 4. A pattern consists of a sequence of notes.
  897.  
  898.   A pattern contains the data that says when the notes should be played, how
  899. long they should be played for, at what pitch, with what instrument, should
  900. there be ADSR, should there be bending (portamento) of the notes etc. Each
  901. pattern is ended with a $ff byte, and when this is encountered, the next
  902. pattern in the track will be played. Each note has up to a 4 byte
  903. specification.
  904.  
  905. - The first byte is always the length of the note from 0-31 or 0-$1f in hex.
  906.   You will notice that the top three bits are not used for the length of the
  907.   note, so they are used for other things.
  908. - Bit#5 signals no release needed. - Bit#6 signals that this note is
  909.   appended to the last one (no attack/etc). - Bit#7 signals that a new
  910.   instrument or portamento is coming up.
  911.  
  912. - The second byte is an optional byte, and it holds the instument number to
  913.   use or the portamento value (ie a bended note). This byte will be needed
  914.   according to whether bit#7 of the first byte is set or not...ie if the 1st
  915.   byte was negative, then this byte is needed.
  916.   - If the second byte is positive, then this is the new instrument number.
  917.   - If the second byte is negative, then this is a bended note (portamento).
  918.     and the value is the speed of the portamento (except for bits #7 and #0)
  919.     Bit #0 of the portamento byte determines the direction of the bend.
  920.     - Bit#0 = 0 then portamento is up.
  921.     - Bit#0 = 1 then portamento is down.
  922.  
  923. - The third byte of the specification is the pitch of the note. A pitch of
  924.   0 is the lowest C possible. A pitch of 12 or $C(hex) is the next highest
  925.   C above that. These pitches are denoted fairly universally as eg. 'C-1' and
  926.   for sharps eg. 'D#3'. Notice that this routine uses pitches of higher than
  927.   72 ($48) which is c-6 :-)
  928.  
  929. - The fourth byte if it exists will denote the end of the pattern.
  930.   ie. If the next byte is a $ff, then this is the end of the pattern.
  931.  
  932. NOTE: I have labelled the various bytes with numbers for convenience. Bear
  933. in mind that some of these are optional, so if the second byte is not needed,
  934. then I will say that the pitch of the note coming up is the 'third byte',
  935. even though it isn't really.
  936.  
  937. Okay, here are some examples:
  938.  
  939. eg. $84,$04,$24 means that the length of the note is 4 (from the lower 5 bits
  940.     of the first byte), that the instrument to use is instrument number 4
  941.     (the second byte, as indicated by bit #7 of the first byte), and that the
  942.     pitch of the note is $24 or c-3.
  943. eg. $D6,$98,$25,$FF means that the length of the note is 22 ($16), that this
  944.     note should be appended to the last, that the second byte is a portamento
  945.     (as both 1st and 2nd bytes -ve!), that the portamento is going up (as
  946.     bit#0 = 0) with a speed of 24 ($18), that the pitch of the note is $25
  947.     or c#3, and that this is the end of the pattern.
  948.  
  949. It doesn't get any harder than that!! Did you realise that this is exactly
  950. the way that Rob Hubbard made the music!! He worked out some musical ideas
  951. on his cheap (musical) keyboard, and typed the notes into his assembler in
  952. hex, just like this.
  953.  
  954.  
  955. 5. The instruments are an 8 byte data structure.
  956.  
  957.   You are looking at the label 'instr' at the bottom of the sourcecode. The
  958. 8 bytes which come along first are instrument numnber 0, the next 8 define
  959. instrument number 1, etc. Here are the meanings of the bytes, but I suggest
  960. that you check out your programming manuals if you are unfamiliar with these:
  961.  
  962. - Byte 0 is the pulse width low byte, and
  963. - Byte 1 is the pulse width high byte. (also see byte 7).
  964.  
  965. - Byte 2 is the control register byte.
  966.   This specifies which type of sound should be used; sine, sawtooth etc.
  967.  
  968. - Byte 3 is the attack and decay values, and
  969. - Byte 4 is the sustain and release values.
  970.   The note's volume is altered according to these values. When the attack and
  971.   decay are over, the volume of the note is held at the sustain level. When
  972.   length of a note is over, a release is done through the 'gate' bit in SID.
  973.  
  974. - Byte 5 is the vibrato depth for the instrument.
  975.  
  976. - Byte 6 is the pulse speed.
  977.   Timbre is created by changing the shape of the waveform each 50th of a
  978.   second, and this is the most common way of achieving it. The shape of
  979.   the pulse waveform changes from square to a very rectangular at a speed
  980.   according to this byte.
  981.   N.B. if you are interested in how the pulse value number works, then
  982.   e-mail me sometime, as I found this out (exhaustively) a few days ago!
  983.  
  984. - Byte 7 is the instrument fx byte, and is the major thing which changes
  985.   between different music routines. Each bit in this byte determines whether
  986.   this instrument will have a certain effect in it.
  987.   - Bit#0 signals that this is a drum. Drums are made from a noise channel
  988.     and also a fast frequency down, with fast decay. Bass drums use a square
  989.     wave, and only the first 50th of a second is a noise channel. This is
  990.     the tell-tale instrument that gives away a Rob Hubbard routine! Hihats
  991.     and other drums use noise all the time.
  992.   - Bit#1 signals a 'skydive'. This is a slower frequency down, that I think
  993.     sounds like somebody yelling as they fall out of a plane .. AHHHHhhhhgh..
  994.     ..hence I call it a skydive!!
  995.   - Bit#2 signals an octave arpeggio. It's a very limited arpeggio routine in
  996.     this song. Listen for the arpeggio and the skydive when combined, which
  997.     is used alot in Hubbard songs.
  998.   - All the other bits have no meaning in this music, but were used alot in
  999.     later music for the fx.
  1000.  
  1001. A big reason that I presented this early routine, was because there was not
  1002. too much in the way of special fx to confuse you. As a result, you can
  1003. concentrate on the guts of the code instead of the special fx :-)
  1004.  
  1005.  
  1006. How the sourcecode works:
  1007. ************************
  1008.  
  1009.   The routines at the top of the sourcecode are concerned with turning the
  1010. music on and off, and you will see that this is done through a variable called
  1011. 'mstatus' (or music status). If mstatus is set to $C0, then the music is
  1012. turned off and SID is quietened, thereafter mstatus is set to $80 which means
  1013. that the music is still off, but SID doesn't need to be quietened again. When
  1014. the music is initialized, then mstatus is given a value of $40 which kicks in
  1015. the further initialization stuff. If mstatus is any other value, then the
  1016. music is being played. For any of the initialization stuff to have any meaning
  1017. to you, you ofcourse have to understand the rest of the playroutine :-)
  1018.  
  1019.   After we have got past the on/off/init stuff, we are at the label called
  1020. 'contplay' at around line 100. The first thing you should notice is that this
  1021. is the start of a huge loop that is done *3* times - once for each channel.
  1022. The loop really *is* huge, as it ends right on the last few lines of the code
  1023. :-)
  1024.  
  1025.   Now that we are talking about routines within the loop, we are talking about
  1026. these routines being applied to the channels independantly. There are 2 main
  1027. routines within the loop, one is called NoteWork, and the other is called
  1028. SoundWork. NoteWork checks to see whether a new note is needed on this
  1029. channel, and if it is, then the notedata is fetched and stuff is initialized.
  1030. If no note is needed, then SoundWork is called which processes the instruments
  1031. and does the portamento.
  1032.  
  1033.   NoteWork first checks the speed at which the notes are fetched. If the delay
  1034. is still occurring, then new notes are not needed and soundwork is called.
  1035. N.B. that the speed for Monty on the Run is 1, which means that a note of
  1036. length $1f will last for 64 calls to the routine (ie just over a second). If
  1037. the speed of the song is reset, then NoteWork decrements the length of the
  1038. current note. When the length of the current note hits $ff (-1) then a new
  1039. note is needed, otherwise SoundWork is jumped to.
  1040.  
  1041.   The data for a new note is collected at the label 'getnewnote'. In the
  1042. simplest case, this involves getting the next bytes of data from the current
  1043. pattern on this channel, but if the end of the pattern is reached, then the
  1044. next pattern number is fetched by reference to the current position within
  1045. this channel's track. In an even more complex situation, the end of a track is
  1046. reached, and the current position needs to be reset to 0 before the next
  1047. pattern number can be found.
  1048.  
  1049.   You can see quite clearly in this part of the routine where the length of
  1050. the note is collected, and it is determined whether a 2nd byte is needed,
  1051. where the pitch is collected, and the end of the song checked. You can also
  1052. see where some of the data is collected from the current instrument and jammed
  1053. into the SID registers.
  1054.  
  1055.   SoundWork is called if no new notes are needed, and it processes the
  1056. instruments and does the portamento etc. This part of the routine is neatly
  1057. expressed in sections which are really well commented and quite easy to
  1058. understand.
  1059.  
  1060. - The first thing that occurs in SoundWork is that the 'gate bit' of SID is
  1061.   set when the length of the note is over - this causes a release of the note.
  1062.  
  1063. - The vibrato routine is quite inefficient, but it's pretty good for 1985!
  1064.   Ofcourse vibrato is implemented by raising and lowering the pitch of the
  1065.   note ever-so-slightly causing the note to 'float'. The amount of the
  1066.   vibrato is determined in the current instrument.
  1067.  
  1068. - The pulsework routine changes the pulsewidth between square wave and very
  1069.   rectangular wave according to the pulsespeed in the current instrument.
  1070.   (ie. it changes the sound of the instrument and thus alters the 'timbre')
  1071.   The routine goes backwards and forwards between the two; and switches
  1072.   when one extremity is reached. It's interesting to note that the current
  1073.   values of the pulse width are actually stored in the instrument :-)
  1074.  
  1075. - Portamento is achieved by adding/subtracting an amount of frequency to
  1076.   the current frequency each time this part of the routine is called.
  1077.  
  1078. - The instrument fx routines are also really easy to figure out, as they are
  1079.   well commented. Both the drums and the skydive do a very fast frequency
  1080.   down, so it is the most significant byte of the frequency which is reduced
  1081.   .. and not 16-bit maths (math?!) The arpeggio is only an octave arpeggio,
  1082.   so for the first 50th of a second, the current note is played, and for
  1083.   the next 50th of a second, current note+12 is played, followed by the
  1084.   current note again etc.
  1085.   ( If you don't know what an arpeggio is..it's generally when the notes of )
  1086.   ( a chord are played individually in a rapid succession. It produces a    )
  1087.   ( 'full' sound depending on the speed of the arpeggio. In most cases the  )
  1088.   ( note is changed 50 times per second, which gives a very nice sound. If  )
  1089.   ( you have listened to some computer music, then you will have definately )
  1090.   ( listened to an arpeggios all the time, even if you don't realize it!    )
  1091.  
  1092.  
  1093. Final Thoughts:
  1094. **************
  1095.  
  1096.   *Bounce* I'm finally near the end of this article! It has been alot of work
  1097. to try to explain this routine, but I'm glad that I've done it *grin* If you
  1098. have any questions then please feel free to e-mail me, or even e-mail Craig if
  1099. it's after August 1993 and I'll make sure that I leave a forwarding address
  1100. with him. Also, please feel free to e-mail me and tell me what you think of
  1101. this article. I will only be bothered writing more of the same if I know that
  1102. someone is finding them useful/interesting. Also e-mail me if you are
  1103. interested in Amiga or ST music too, as I've done alot on both of those
  1104. machines.
  1105.  
  1106.   I'm not sure whether Craig will be putting the actual sourcecode below this
  1107. text, or in some kind of Appendix. In either case, I SHALL take all legal
  1108. responsibilty for publishing Rob Hubbard's routine. Craig was quite reluctant
  1109. to publish the routine in his net-mag because of copyright reasons. As a
  1110. post-graduate law student that will be working as a commercial lawyer
  1111. (attourney for Americans :) specializing in copyright/patents for computer
  1112. software/hardware starting August this year, I don't believe that there are
  1113. any practical legal consequences for me.
  1114.  
  1115.   I would have given an arm or a leg for a commented Rob Hubbard sourcecode in
  1116. the past, so I hope you enjoy this valuable offering.
  1117. -----------------------------------------------------------------------------
  1118. ;rob hubbard
  1119. ;monty on the run music driver
  1120.  
  1121. ;this player was used (with small mods)
  1122. ;for his first approx 30 musix
  1123.  
  1124. .org $8000
  1125. .obj motr
  1126.  
  1127.  jmp initmusic
  1128.  jmp playmusic
  1129.  jmp musicoff
  1130.  
  1131.  
  1132. ;====================================
  1133. ;init music
  1134.  
  1135. initmusic =*
  1136.  
  1137.   lda #$00         ;music num
  1138.   ldy #$00
  1139.   asl
  1140.   sta tempstore
  1141.   asl
  1142.   clc
  1143.   adc tempstore    ;now music num*6
  1144.   tax
  1145.  
  1146. - lda songs,x      ;copy ptrs to this
  1147.   sta currtrkhi,y  ;music's tracks to
  1148.   inx              ;current tracks
  1149.   iny
  1150.   cpy #$06
  1151.   bne -
  1152.  
  1153.   lda #$00         ;clear control regs
  1154.   sta $d404
  1155.   sta $d40b
  1156.   sta $d412
  1157.   sta $d417
  1158.  
  1159.   lda #$0f         ;full volume
  1160.   sta $d418
  1161.  
  1162.   lda #$40         ;flag init music
  1163.   sta mstatus
  1164.  
  1165.   rts
  1166.  
  1167.  
  1168. ;====================================
  1169. ;music off
  1170.  
  1171. musicoff =*
  1172.  
  1173.   lda #$c0         ;flag music off
  1174.   sta mstatus
  1175.   rts
  1176.  
  1177.  
  1178. ;====================================
  1179. ;play music
  1180.  
  1181. playmusic =*
  1182.  
  1183.   inc counter
  1184.  
  1185.   bit mstatus      ;test music status
  1186.   bmi moff         ;$80 and $c0 is off
  1187.   bvc contplay     ;$40 init, else play
  1188.  
  1189.  
  1190. ;==========
  1191. ;init the song (mstatus $40)
  1192.  
  1193.   lda #$00         ;init counter
  1194.   sta counter
  1195.  
  1196.   ldx #3-1
  1197. - sta posoffset,x  ;init pos offsets
  1198.   sta patoffset,x  ;init pat offsets
  1199.   sta lengthleft,x ;get note right away
  1200.   sta notenum,x
  1201.   dex
  1202.   bpl -
  1203.  
  1204.   sta mstatus      ;signal music play
  1205.   jmp contplay
  1206.  
  1207.  
  1208. ;==========
  1209. ;music is off (mstatus $80 or $c0)
  1210.  
  1211. moff =*
  1212.  
  1213.   bvc +            ;if mstatus $c0 then
  1214.   lda #$00
  1215.   sta $d404        ;kill voice 1,2,3
  1216.   sta $d40b        ;control registers
  1217.   sta $d412
  1218.  
  1219.   lda #$0f         ;full volume still
  1220.   sta $d418
  1221.  
  1222.   lda #$80         ;flag no need to kill
  1223.   sta mstatus      ;sound next time
  1224.  
  1225. + jmp musicend     ;end
  1226.  
  1227.  
  1228. ;==========
  1229. ;music is playing (mstatus otherwise)
  1230.  
  1231. contplay =*
  1232.  
  1233.   ldx #3-1         ;number of chanels
  1234.  
  1235.   dec speed        ;check the speed
  1236.   bpl mainloop
  1237.  
  1238.   lda resetspd     ;reset speed if needed
  1239.   sta speed
  1240.  
  1241.  
  1242. mainloop =*
  1243.  
  1244.   lda regoffsets,x ;save offset to regs
  1245.   sta tmpregofst   ;for this channel
  1246.   tay
  1247.  
  1248.  
  1249. ;check whether a new note is needed
  1250.  
  1251.   lda speed        ;if speed not reset
  1252.   cmp resetspd     ;then skip notework
  1253.   beq checknewnote
  1254.   jmp vibrato
  1255.  
  1256. checknewnote =*
  1257.  
  1258.   lda currtrkhi,x  ;put base addr.w of
  1259.   sta $02          ;this track in $2
  1260.   lda currtrklo,x
  1261.   sta $03
  1262.  
  1263.   dec lengthleft,x ;check whether a new
  1264.   bmi getnewnote   ;note is needed
  1265.  
  1266.   jmp soundwork    ;no new note needed
  1267.  
  1268.  
  1269. ;==========
  1270. ;notework
  1271. ;a new note is needed. get the pattern
  1272. ;number/cc from this position
  1273.  
  1274. getnewnote =*
  1275.  
  1276.   ldy posoffset,x  ;get the data from
  1277.   lda ($02),y      ;the current position
  1278.  
  1279.   cmp #$ff         ;pos $ff restarts
  1280.   beq restart
  1281.  
  1282.   cmp #$fe         ;pos $fe stops music
  1283.   bne getnotedata  ;on all channels
  1284.   jmp musicend
  1285.  
  1286. ;cc of $ff restarts this track from the
  1287. ;first position
  1288.  
  1289. restart =*
  1290.  
  1291.   lda #$00         ;get note immediately
  1292.   sta lengthleft,x ;and reset pat,pos
  1293.   sta posoffset,x
  1294.   sta patoffset,x
  1295.   jmp getnewnote
  1296.  
  1297.  
  1298. ;get the note data from this pattern
  1299.  
  1300. getnotedata =*
  1301.  
  1302.   tay
  1303.   lda patptl,y     ;put base addr.w of
  1304.   sta $04          ;the pattern in $4
  1305.   lda patpth,y
  1306.   sta $05
  1307.  
  1308.   lda #$00         ;default no portamento
  1309.   sta portaval,x
  1310.  
  1311.   ldy patoffset,x  ;get offset into ptn
  1312.  
  1313.   lda #$ff         ;default no append
  1314.   sta appendfl
  1315.  
  1316. ;1st byte is the length of the note 0-31
  1317. ;bit5 signals no release (see sndwork)
  1318. ;bit6 signals appended note
  1319. ;bit7 signals a new instrument
  1320. ;     or portamento coming up
  1321.  
  1322.   lda ($04),y      ;get length of note
  1323.   sta savelnthcc,x
  1324.   sta templnthcc
  1325.   and #$1f
  1326.   sta lengthleft,x
  1327.  
  1328.   bit templnthcc   ;test for append
  1329.   bvs appendnote
  1330.  
  1331.   inc patoffset,x  ;pt to next data
  1332.  
  1333.   lda templnthcc   ;2nd byte needed?
  1334.   bpl getpitch
  1335.  
  1336. ;2nd byte needed as 1st byte negative
  1337. ;2nd byte is the instrument number(+ve)
  1338. ;or portamento speed(-ve)
  1339.  
  1340.   iny
  1341.   lda ($04),y      ;get instr/portamento
  1342.   bpl +
  1343.  
  1344.   sta portaval,x   ;save portamento val
  1345.   jmp ++
  1346.  
  1347. + sta instrnr,x    ;save instr nr
  1348.  
  1349. + inc patoffset,x
  1350.  
  1351. ;3rd byte is the pitch of the note
  1352. ;get the 'base frequency' here
  1353.  
  1354. getpitch =*
  1355.  
  1356.   iny
  1357.   lda ($04),y      ;get pitch of note
  1358.   sta notenum,x
  1359.   asl              ;pitch*2
  1360.   tay
  1361.   lda frequenzlo,y ;save the appropriate
  1362.   sta tempfreq     ;base frequency
  1363.   lda frequenzhi,y
  1364.   ldy tmpregofst
  1365.   sta $d401,y
  1366.   sta savefreqhi,x
  1367.   lda tempfreq
  1368.   sta $d400,y
  1369.   sta savefreqlo,x
  1370.   jmp +
  1371.  
  1372. appendnote =*
  1373.  
  1374.   dec appendfl     ;clever eh?
  1375.  
  1376.  
  1377. ;fetch all the initial values from the
  1378. ;instrument data structure
  1379.  
  1380. + ldy tmpregofst
  1381.   lda instrnr,x    ;instr num
  1382.   stx tempstore
  1383.   asl              ;instr num*8
  1384.   asl
  1385.   asl
  1386.   tax
  1387.  
  1388.   lda instr+2,x    ;get control reg val
  1389.   sta tempctrl
  1390.   lda instr+2,x
  1391.   and appendfl     ;implement append
  1392.   sta $d404,y
  1393.  
  1394.   lda instr+0,x    ;get pulse width lo
  1395.   sta $d402,y
  1396.  
  1397.   lda instr+1,x    ;get pulse width hi
  1398.   sta $d403,y
  1399.  
  1400.   lda instr+3,x    ;get attack/decay
  1401.   sta $d405,y
  1402.  
  1403.   lda instr+4,x    ;get sustain/release
  1404.   sta $d406,y
  1405.  
  1406.   ldx tempstore    ;save control reg val
  1407.   lda tempctrl
  1408.   sta voicectrl,x
  1409.  
  1410.  
  1411. ;4th byte checks for the end of pattern
  1412. ;if eop found, inc the position and
  1413. ;reset patoffset for new pattern
  1414.  
  1415.   inc patoffset,x  ;preview 4th byte
  1416.   ldy patoffset,x
  1417.   lda ($04),y
  1418.  
  1419.   cmp #$ff         ;check for eop
  1420.   bne +
  1421.  
  1422.   lda #$00         ;end of pat reached
  1423.   sta patoffset,x  ;inc position for
  1424.   inc posoffset,x  ;the next time
  1425.  
  1426. + jmp loopcont
  1427.  
  1428.  
  1429. ;==========
  1430. ;soundwork
  1431. ;the instrument and effects processing
  1432. ;routine when no new note was needed
  1433.  
  1434. soundwork =*
  1435.  
  1436. ;release routine
  1437. ;set off a release when the length of
  1438. ;the note is exceeded
  1439. ;bit4 of the 1st note-byte can specify
  1440. ;for no release
  1441.  
  1442.   ldy tmpregofst
  1443.  
  1444.   lda savelnthcc,x ;check for no release
  1445.   and #$20         ;specified
  1446.   bne vibrato
  1447.  
  1448.   lda lengthleft,x ;check for length of
  1449.   bne vibrato      ;exceeded
  1450.  
  1451.   lda voicectrl,x  ;length exceeded so
  1452.   and #$fe         ;start the release
  1453.   sta $d404,y      ;and kill adsr
  1454.   lda #$00
  1455.   sta $d405,y
  1456.   sta $d406,y
  1457.  
  1458.  
  1459. ;vibrato routine
  1460. ;(does alot of work)
  1461.  
  1462. vibrato =*
  1463.  
  1464.   lda instrnr,x    ;instr num
  1465.   asl
  1466.   asl
  1467.   asl              ;instr num*8
  1468.   tay
  1469.   sty instnumby8   ;save instr num*8
  1470.  
  1471.   lda instr+7,y    ;get instr fx byte
  1472.   sta instrfx
  1473.  
  1474.   lda instr+6,y    ;get pulse speed
  1475.   sta pulsevalue
  1476.  
  1477.   lda instr+5,y    ;get vibrato depth
  1478.   sta vibrdepth
  1479.   beq pulsework    ;check for no vibrato
  1480.  
  1481.   lda counter      ;this is clever!!
  1482.   and #7           ;the counter's turned
  1483.   cmp #4           ;into an oscillating
  1484.   bcc +            ;value (01233210)
  1485.   eor #7
  1486. + sta oscilatval
  1487.  
  1488.   lda notenum,x    ;get base note
  1489.   asl              ;note*2
  1490.   tay              ;get diff btw note
  1491.   sec              ;and note+1 frequency
  1492.   lda frequenzlo+2,y
  1493.   sbc frequenzlo,y
  1494.   sta tmpvdiflo
  1495.   lda frequenzhi+2,y
  1496.   sbc frequenzhi,y
  1497.  
  1498. - lsr              ;divide difference by
  1499.   ror tmpvdiflo    ;2 for each vibrdepth
  1500.   dec vibrdepth
  1501.   bpl -
  1502.   sta tmpvdifhi
  1503.  
  1504.   lda frequenzlo,y ;save note frequency
  1505.   sta tmpvfrqlo
  1506.   lda frequenzhi,y
  1507.   sta tmpvfrqhi
  1508.  
  1509.   lda savelnthcc,x ;no vibrato if note
  1510.   and #$1f         ;length less than 8
  1511.   cmp #8
  1512.   bcc +
  1513.  
  1514.   ldy oscilatval
  1515.  
  1516. - dey              ;depending on the osc
  1517.   bmi +            ;value, add the vibr
  1518.   clc              ;freq that many times
  1519.   lda tmpvfrqlo    ;to the base freq
  1520.   adc tmpvdiflo
  1521.   sta tmpvfrqlo
  1522.   lda tmpvfrqhi
  1523.   adc tmpvdifhi
  1524.   sta tmpvfrqhi
  1525.   jmp -
  1526.  
  1527. + ldy tmpregofst   ;save the final
  1528.   lda tmpvfrqlo    ;frequencies
  1529.   sta $d400,y
  1530.   lda tmpvfrqhi
  1531.   sta $d401,y
  1532.  
  1533.  
  1534. ;pulse-width timbre routine
  1535. ;depending on the control/speed byte in
  1536. ;the instrument datastructure, the pulse
  1537. ;width is of course inc/decremented to
  1538. ;produce timbre
  1539.  
  1540. ;strangely the delay value is also the
  1541. ;size of the inc/decrements
  1542.  
  1543. pulsework =*
  1544.  
  1545.   lda pulsevalue   ;check for pulsework
  1546.   beq portamento   ;needed this instr
  1547.  
  1548.   ldy instnumby8
  1549.   and #$1f
  1550.   dec pulsedelay,x ;pulsedelay-1
  1551.   bpl portamento
  1552.  
  1553.   sta pulsedelay,x ;reset pulsedelay
  1554.  
  1555.   lda pulsevalue   ;restrict pulse speed
  1556.   and #$e0         ;from $00-$1f
  1557.   sta pulsespeed
  1558.  
  1559.   lda pulsedir,x   ;pulsedir 0 is up and
  1560.   bne pulsedown    ;1 is down
  1561.  
  1562.   lda pulsespeed   ;pulse width up
  1563.   clc
  1564.   adc instr+0,y    ;add the pulsespeed
  1565.   pha              ;to the pulse width
  1566.   lda instr+1,y
  1567.   adc #$00
  1568.   and #$0f
  1569.   pha
  1570.   cmp #$0e         ;go pulsedown when
  1571.   bne dumpulse     ;the pulse value
  1572.   inc pulsedir,x   ;reaches max ($0exx)
  1573.   jmp dumpulse
  1574.  
  1575. pulsedown =*
  1576.  
  1577.   sec              ;pulse width down
  1578.   lda instr+0,y
  1579.   sbc pulsespeed   ;sub the pulsespeed
  1580.   pha              ;from the pulse width
  1581.   lda instr+1,y
  1582.   sbc #$00
  1583.   and #$0f
  1584.   pha
  1585.   cmp #$08         ;go pulseup when
  1586.   bne dumpulse     ;the pulse value
  1587.   dec pulsedir,x   ;reaches min ($08xx)
  1588.  
  1589. dumpulse =*
  1590.  
  1591.   stx tempstore    ;dump pulse width to
  1592.   ldx tmpregofst   ;chip and back into
  1593.   pla              ;the instr data str
  1594.   sta instr+1,y
  1595.   sta $d403,x
  1596.   pla
  1597.   sta instr+0,y
  1598.   sta $d402,x
  1599.   ldx tempstore
  1600.  
  1601.  
  1602. ;portamento routine
  1603. ;portamento comes from the second byte
  1604. ;if it's a negative value
  1605.  
  1606. portamento =*
  1607.  
  1608.   ldy tmpregofst
  1609.   lda portaval,x   ;check for portamento
  1610.   beq drums        ;none
  1611.  
  1612.   and #$7e         ;toad unwanted bits
  1613.   sta tempstore
  1614.  
  1615.   lda portaval,x   ;bit0 signals up/down
  1616.   and #$01
  1617.   beq portup
  1618.  
  1619.   sec              ;portamento down
  1620.   lda savefreqlo,x ;sub portaval from
  1621.   sbc tempstore    ;current frequency
  1622.   sta savefreqlo,x
  1623.   sta $d400,y
  1624.   lda savefreqhi,x
  1625.   sbc #$00         ;(word arithmetic)
  1626.   sta savefreqhi,x
  1627.   sta $d401,y
  1628.   jmp drums
  1629.  
  1630. portup =*
  1631.  
  1632.   clc              ;portamento up
  1633.   lda savefreqlo,x ;add portval to
  1634.   adc tempstore    ;current frequency
  1635.   sta savefreqlo,x
  1636.   sta $d400,y
  1637.   lda savefreqhi,x
  1638.   adc #$00
  1639.   sta savefreqhi,x
  1640.   sta $d401,y
  1641.  
  1642.  
  1643. ;bit0 instrfx are the drum routines
  1644. ;the actual drum timbre depends on the
  1645. ;crtl register value for the instrument:
  1646. ;ctrlreg 0 is always noise
  1647. ;ctrlreg x is noise for 1st vbl and x
  1648. ;from then on
  1649.  
  1650. ;see that the drum is made by rapid hi
  1651. ;to low frequency slide with fast attack
  1652. ;and decay
  1653.  
  1654. drums =*
  1655.  
  1656.   lda instrfx      ;check if drums
  1657.   and #$01         ;needed this instr
  1658.   beq skydive
  1659.  
  1660.   lda savefreqhi,x ;don't bother if freq
  1661.   beq skydive      ;can't go any lower
  1662.  
  1663.   lda lengthleft,x ;or if the note has
  1664.   beq skydive      ;finished
  1665.  
  1666.   lda savelnthcc,x ;check if this is the
  1667.   and #$1f         ;first vbl for this
  1668.   sec              ;instrument-note
  1669.   sbc #$01
  1670.   cmp lengthleft,x
  1671.   ldy tmpregofst
  1672.   bcc firstime
  1673.  
  1674.   lda savefreqhi,x ;not the first time
  1675.   dec savefreqhi,x ;so dec freqhi for
  1676.   sta $d401,y      ;drum sound
  1677.  
  1678.   lda voicectrl,x  ;if ctrlreg is 0 then
  1679.   and #$fe         ;noise is used always
  1680.   bne dumpctrl
  1681.  
  1682. firstime =*
  1683.  
  1684.   lda savefreqhi,x ;noise is used for
  1685.   sta $d401,y      ;the first vbl also
  1686.   lda #$80         ;(set noise)
  1687.  
  1688. dumpctrl =*
  1689.  
  1690.   sta $d404,y
  1691.  
  1692.  
  1693. ;bit1 instrfx is the skydive
  1694. ;a long portamento-down from the note
  1695. ;to zerofreq
  1696.  
  1697. skydive =*
  1698.  
  1699.   lda instrfx      ;check if skydive
  1700.   and #$02         ;needed this instr
  1701.   beq octarp
  1702.  
  1703.   lda counter      ;every 2nd vbl
  1704.   and #$01
  1705.   beq octarp
  1706.  
  1707.   lda savefreqhi,x ;check if skydive
  1708.   beq octarp        ;already complete
  1709.  
  1710.   dec savefreqhi,x ;decr and save the
  1711.   ldy tmpregofst   ;high byte freq
  1712.   sta $d401,y
  1713.  
  1714.  
  1715. ;bit2 instrfx is an octave arpeggio
  1716. ;pretty tame huh?
  1717.  
  1718. octarp =*
  1719.  
  1720.   lda instrfx      ;check if arpt needed
  1721.   and #$04
  1722.   beq loopcont
  1723.  
  1724.   lda counter      ;only 2 arpt values
  1725.   and #$01
  1726.   beq +
  1727.  
  1728.   lda notenum,x    ;odd, note+12
  1729.   clc
  1730.   adc #$0c
  1731.   jmp ++
  1732.  
  1733. + lda notenum,x    ;even, note
  1734.  
  1735. + asl              ;dump the corresponding
  1736.   tay              ;frequencies
  1737.   lda frequenzlo,y
  1738.   sta tempfreq
  1739.   lda frequenzhi,y
  1740.   ldy tmpregofst
  1741.   sta $d401,y
  1742.   lda tempfreq
  1743.   sta $d400,y
  1744.  
  1745.  
  1746. ;==========
  1747. ;end of dbf loop
  1748.  
  1749. loopcont =*
  1750.  
  1751.   dex              ;dbf mainloop
  1752.   bmi musicend
  1753.   jmp mainloop
  1754.  
  1755. musicend =*
  1756.  
  1757.   rts
  1758.  
  1759.  
  1760. ;====================================
  1761. ;frequenz data
  1762. ;====================================
  1763.  
  1764. frequenzlo .byt $16
  1765. frequenzhi .byt $01
  1766.  .byt $27,$01,$38,$01,$4b,$01
  1767.  .byt $5f,$01,$73,$01,$8a,$01,$a1,$01
  1768.  .byt $ba,$01,$d4,$01,$f0,$01,$0e,$02
  1769.  .byt $2d,$02,$4e,$02,$71,$02,$96,$02
  1770.  .byt $bd,$02,$e7,$02,$13,$03,$42,$03
  1771.  .byt $74,$03,$a9,$03,$e0,$03,$1b,$04
  1772.  .byt $5a,$04,$9b,$04,$e2,$04,$2c,$05
  1773.  .byt $7b,$05,$ce,$05,$27,$06,$85,$06
  1774.  .byt $e8,$06,$51,$07,$c1,$07,$37,$08
  1775.  .byt $b4,$08,$37,$09,$c4,$09,$57,$0a
  1776.  .byt $f5,$0a,$9c,$0b,$4e,$0c,$09,$0d
  1777.  .byt $d0,$0d,$a3,$0e,$82,$0f,$6e,$10
  1778.  .byt $68,$11,$6e,$12,$88,$13,$af,$14
  1779.  .byt $eb,$15,$39,$17,$9c,$18,$13,$1a
  1780.  .byt $a1,$1b,$46,$1d,$04,$1f,$dc,$20
  1781.  .byt $d0,$22,$dc,$24,$10,$27,$5e,$29
  1782.  .byt $d6,$2b,$72,$2e,$38,$31,$26,$34
  1783.  .byt $42,$37,$8c,$3a,$08,$3e,$b8,$41
  1784.  .byt $a0,$45,$b8,$49,$20,$4e,$bc,$52
  1785.  .byt $ac,$57,$e4,$5c,$70,$62,$4c,$68
  1786.  .byt $84,$6e,$18,$75,$10,$7c,$70,$83
  1787.  .byt $40,$8b,$70,$93,$40,$9c,$78,$a5
  1788.  .byt $58,$af,$c8,$b9,$e0,$c4,$98,$d0
  1789.  .byt $08,$dd,$30,$ea,$20,$f8,$2e,$fd
  1790.  
  1791.  
  1792. regoffsets .byt $00,$07,$0e
  1793. tmpregofst .byt $00
  1794. posoffset  .byt $00,$00,$00
  1795. patoffset  .byt $00,$00,$00
  1796. lengthleft .byt $00,$00,$00
  1797. savelnthcc .byt $00,$00,$00
  1798. voicectrl  .byt $00,$00,$00
  1799. notenum    .byt $00,$00,$00
  1800. instrnr    .byt $00,$00,$00
  1801. appendfl   .byt $00
  1802. templnthcc .byt $00
  1803. tempfreq   .byt $00
  1804. tempstore  .byt $00
  1805. tempctrl   .byt $00
  1806. vibrdepth  .byt $00
  1807. pulsevalue .byt $00
  1808. tmpvdiflo  .byt $00
  1809. tmpvdifhi  .byt $00
  1810. tmpvfrqlo  .byt $00
  1811. tmpvfrqhi  .byt $00
  1812. oscilatval .byt $00
  1813. pulsedelay .byt $00,$00,$00
  1814. pulsedir   .byt $00,$00,$00
  1815. speed      .byt $00
  1816. resetspd   .byt $01
  1817. instnumby8 .byt $00
  1818. mstatus    .byt $c0
  1819. savefreqhi .byt $00,$00,$00
  1820. savefreqlo .byt $00,$00,$00
  1821. portaval   .byt $00,$00,$00
  1822. instrfx    .byt $00
  1823. pulsespeed .byt $00
  1824. counter    .byt $00
  1825. currtrkhi  .byt $00,$00,$00
  1826. currtrklo  .byt $00,$00,$00
  1827.  
  1828.  
  1829. ;====================================
  1830. ;monty on the run main theme
  1831. ;====================================
  1832.  
  1833. songs =*
  1834.  .byt <montymaintr1
  1835.  .byt <montymaintr2
  1836.  .byt <montymaintr3
  1837.  .byt >montymaintr1
  1838.  .byt >montymaintr2
  1839.  .byt >montymaintr3
  1840.  
  1841.  
  1842. ;====================================
  1843. ;pointers to the patterns
  1844.  
  1845. ;low pointers
  1846. patptl =*
  1847.  .byt <ptn00
  1848.  .byt <ptn01
  1849.  .byt <ptn02
  1850.  .byt <ptn03
  1851.  .byt <ptn04
  1852.  .byt <ptn05
  1853.  .byt <ptn06
  1854.  .byt <ptn07
  1855.  .byt <ptn08
  1856.  .byt <ptn09
  1857.  .byt <ptn0a
  1858.  .byt <ptn0b
  1859.  .byt <ptn0c
  1860.  .byt <ptn0d
  1861.  .byt <ptn0e
  1862.  .byt <ptn0f
  1863.  .byt <ptn10
  1864.  .byt <ptn11
  1865.  .byt <ptn12
  1866.  .byt <ptn13
  1867.  .byt <ptn14
  1868.  .byt <ptn15
  1869.  .byt <ptn16
  1870.  .byt <ptn17
  1871.  .byt <ptn18
  1872.  .byt <ptn19
  1873.  .byt <ptn1a
  1874.  .byt <ptn1b
  1875.  .byt <ptn1c
  1876.  .byt <ptn1d
  1877.  .byt <ptn1e
  1878.  .byt <ptn1f
  1879.  .byt <ptn20
  1880.  .byt <ptn21
  1881.  .byt <ptn22
  1882.  .byt <ptn23
  1883.  .byt <ptn24
  1884.  .byt <ptn25
  1885.  .byt <ptn26
  1886.  .byt <ptn27
  1887.  .byt <ptn28
  1888.  .byt <ptn29
  1889.  .byt <ptn2a
  1890.  .byt <ptn2b
  1891.  .byt <ptn2c
  1892.  .byt <ptn2d
  1893.  .byt 0
  1894.  .byt <ptn2f
  1895.  .byt <ptn30
  1896.  .byt <ptn31
  1897.  .byt <ptn32
  1898.  .byt <ptn33
  1899.  .byt <ptn34
  1900.  .byt <ptn35
  1901.  .byt <ptn36
  1902.  .byt <ptn37
  1903.  .byt <ptn38
  1904.  .byt <ptn39
  1905.  .byt <ptn3a
  1906.  .byt <ptn3b
  1907.  
  1908. ;high pointers
  1909. patpth =*
  1910.  .byt >ptn00
  1911.  .byt >ptn01
  1912.  .byt >ptn02
  1913.  .byt >ptn03
  1914.  .byt >ptn04
  1915.  .byt >ptn05
  1916.  .byt >ptn06
  1917.  .byt >ptn07
  1918.  .byt >ptn08
  1919.  .byt >ptn09
  1920.  .byt >ptn0a
  1921.  .byt >ptn0b
  1922.  .byt >ptn0c
  1923.  .byt >ptn0d
  1924.  .byt >ptn0e
  1925.  .byt >ptn0f
  1926.  .byt >ptn10
  1927.  .byt >ptn11
  1928.  .byt >ptn12
  1929.  .byt >ptn13
  1930.  .byt >ptn14
  1931.  .byt >ptn15
  1932.  .byt >ptn16
  1933.  .byt >ptn17
  1934.  .byt >ptn18
  1935.  .byt >ptn19
  1936.  .byt >ptn1a
  1937.  .byt >ptn1b
  1938.  .byt >ptn1c
  1939.  .byt >ptn1d
  1940.  .byt >ptn1e
  1941.  .byt >ptn1f
  1942.  .byt >ptn20
  1943.  .byt >ptn21
  1944.  .byt >ptn22
  1945.  .byt >ptn23
  1946.  .byt >ptn24
  1947.  .byt >ptn25
  1948.  .byt >ptn26
  1949.  .byt >ptn27
  1950.  .byt >ptn28
  1951.  .byt >ptn29
  1952.  .byt >ptn2a
  1953.  .byt >ptn2b
  1954.  .byt >ptn2c
  1955.  .byt >ptn2d
  1956.  .byt 0
  1957.  .byt >ptn2f
  1958.  .byt >ptn30
  1959.  .byt >ptn31
  1960.  .byt >ptn32
  1961.  .byt >ptn33
  1962.  .byt >ptn34
  1963.  .byt >ptn35
  1964.  .byt >ptn36
  1965.  .byt >ptn37
  1966.  .byt >ptn38
  1967.  .byt >ptn39
  1968.  .byt >ptn3a
  1969.  .byt >ptn3b
  1970.  
  1971.  
  1972. ;====================================
  1973. ;tracks
  1974. ;====================================
  1975.  
  1976. ;track1
  1977. montymaintr1 =*
  1978.  .byt $11,$14,$17,$1a,$00,$27,$00,$28
  1979.  .byt $03,$05,$00,$27,$00,$28,$03,$05
  1980.  .byt $07,$3a,$14,$17,$00,$27,$00,$28
  1981.  .byt $2f,$30,$31,$31,$32,$33,$33,$34
  1982.  .byt $34,$34,$34,$34,$34,$34,$34,$35
  1983.  .byt $35,$35,$35,$35,$35,$36,$12,$37
  1984.  .byt $38,$09,$2a,$09,$2b,$09,$0a,$09
  1985.  .byt $2a,$09,$2b,$09,$0a,$0d,$0d,$0f
  1986.  .byt $ff
  1987.  
  1988. ;track2
  1989. montymaintr2 =*
  1990.  .byt $12,$15,$18,$1b,$2d,$39,$39
  1991.  .byt $39,$39,$39,$39,$2c,$39,$39,$39
  1992.  .byt $39,$39,$39,$2c,$39,$39,$39,$01
  1993.  .byt $01,$29,$29,$2c,$15,$18,$39,$39
  1994.  .byt $39,$39,$39,$39,$39,$39,$39,$39
  1995.  .byt $39,$39,$39,$39,$39,$39,$39,$39
  1996.  .byt $39,$39,$39,$39,$39,$39,$39,$39
  1997.  .byt $39,$39,$39,$39,$39,$01,$01,$01
  1998.  .byt $29,$39,$39,$39,$01,$01,$01,$29
  1999.  .byt $39,$39,$39,$39,$ff
  2000.  
  2001. ;track3
  2002. montymaintr3 =*
  2003.  .byt $13,$16,$19
  2004.  .byt $1c,$02,$02,$1d,$1e,$02,$02,$1d
  2005.  .byt $1f,$04,$04,$20,$20,$06,$02,$02
  2006.  .byt $1d,$1e,$02,$02,$1d,$1f,$04,$04
  2007.  .byt $20,$20,$06,$08,$08,$08,$08,$21
  2008.  .byt $21,$21,$21,$22,$22,$22,$23,$22
  2009.  .byt $24,$25,$3b,$26,$26,$26,$26,$26
  2010.  .byt $26,$26,$26,$26,$26,$26,$26,$26
  2011.  .byt $26,$26,$26,$02,$02,$1d,$1e,$02
  2012.  .byt $02,$1d,$1f,$2f,$2f,$2f,$2f,$2f
  2013.  .byt $2f,$2f,$2f,$2f,$2f,$2f,$2f,$2f
  2014.  .byt $0b,$0b,$1d,$1d,$0b,$0b,$1d,$0b
  2015.  .byt $0b,$0b,$0c,$0c,$1d,$1d,$1d,$10
  2016.  .byt $0b,$0b,$1d,$1d,$0b,$0b,$1d,$0b
  2017.  .byt $0b,$0b,$0c,$0c,$1d,$1d,$1d,$10
  2018.  .byt $0b,$1d,$0b,$1d,$0b,$1d,$0b,$1d
  2019.  .byt $0b,$0c,$1d,$0b,$0c,$23,$0b,$0b
  2020.  .byt $ff
  2021.  
  2022.  
  2023. ;====================================
  2024. ;patterns
  2025. ;====================================
  2026.  
  2027. ptn00 =*
  2028.  .byt $83,$00,$37,$01,$3e,$01,$3e,$03
  2029.  .byt $3d,$03,$3e,$03,$43,$03,$3e,$03
  2030.  .byt $3d,$03,$3e,$03,$37,$01,$3e,$01
  2031.  .byt $3e,$03,$3d,$03,$3e,$03,$43,$03
  2032.  .byt $42,$03,$43,$03,$45,$03,$46,$01
  2033.  .byt $48,$01,$46,$03,$45,$03,$43,$03
  2034.  .byt $4b,$01,$4d,$01,$4b,$03,$4a,$03
  2035.  .byt $48,$ff
  2036.  
  2037. ptn27 =*
  2038.  .byt $1f,$4a,$ff
  2039.  
  2040. ptn28 =*
  2041.  .byt $03,$46,$01,$48,$01,$46,$03,$45
  2042.  .byt $03,$4a,$0f,$43,$ff
  2043.  
  2044. ptn03 =*
  2045.  .byt $bf,$06
  2046.  .byt $48,$07,$48,$01,$4b,$01,$4a,$01
  2047.  .byt $4b,$01,$4a,$03,$4b,$03,$4d,$03
  2048.  .byt $4b,$03,$4a,$3f,$48,$07,$48,$01
  2049.  .byt $4b,$01,$4a,$01,$4b,$01,$4a,$03
  2050.  .byt $4b,$03,$4d,$03,$4b,$03,$48,$3f
  2051.  .byt $4c,$07,$4c,$01,$4f,$01,$4e,$01
  2052.  .byt $4f,$01,$4e,$03,$4f,$03,$51,$03
  2053.  .byt $4f,$03,$4e,$3f,$4c,$07,$4c,$01
  2054.  .byt $4f,$01,$4e,$01,$4f,$01,$4e,$03
  2055.  .byt $4f,$03,$51,$03,$4f,$03,$4c,$ff
  2056.  
  2057. ptn05 =*
  2058.  .byt $83,$04,$26,$03,$29,$03,$28,$03
  2059.  .byt $29,$03,$26,$03,$35,$03,$34,$03
  2060.  .byt $32,$03,$2d,$03,$30,$03,$2f,$03
  2061.  .byt $30,$03,$2d,$03,$3c,$03,$3b,$03
  2062.  .byt $39,$03,$30,$03,$33,$03,$32,$03
  2063.  .byt $33,$03,$30,$03,$3f,$03,$3e,$03
  2064.  .byt $3c,$03,$46,$03,$45,$03,$43,$03
  2065.  .byt $3a,$03,$39,$03,$37,$03,$2e,$03
  2066.  .byt $2d,$03,$26,$03,$29,$03,$28,$03
  2067.  .byt $29,$03,$26,$03,$35,$03,$34,$03
  2068.  .byt $32,$03,$2d,$03,$30,$03,$2f,$03
  2069.  .byt $30,$03,$2d,$03,$3c,$03,$3b,$03
  2070.  .byt $39,$03,$30,$03,$33,$03,$32,$03
  2071.  .byt $33,$03,$30,$03,$3f,$03,$3e,$03
  2072.  .byt $3c,$03,$34,$03,$37,$03,$36,$03
  2073.  .byt $37,$03,$34,$03,$37,$03,$3a,$03
  2074.  .byt $3d
  2075.  
  2076. ptn3a =*
  2077.  .byt $03,$3e,$07,$3e,$07,$3f,$07
  2078.  .byt $3e,$03,$3c,$07,$3e,$57,$ff
  2079.  
  2080. ptn07 =*
  2081.  .byt $8b
  2082.  .byt $00,$3a,$01,$3a,$01,$3c,$03,$3d
  2083.  .byt $03,$3f,$03,$3d,$03,$3c,$0b,$3a
  2084.  .byt $03,$39,$07,$3a,$81,$06,$4b,$01
  2085.  .byt $4d,$01,$4e,$01,$4d,$01,$4e,$01
  2086.  .byt $4d,$05,$4b,$81,$00,$3a,$01,$3c
  2087.  .byt $01,$3d,$03,$3f,$03,$3d,$03,$3c
  2088.  .byt $03,$3a,$03,$39,$1b,$3a,$0b,$3b
  2089.  .byt $01,$3b,$01,$3d,$03,$3e,$03,$40
  2090.  .byt $03,$3e,$03,$3d,$0b,$3b,$03,$3a
  2091.  .byt $07,$3b,$81,$06,$4c,$01,$4e,$01
  2092.  .byt $4f,$01,$4e,$01,$4f,$01,$4e,$05
  2093.  .byt $4c,$81,$00,$3b,$01,$3d,$01,$3e
  2094.  .byt $03,$40,$03,$3e,$03,$3d,$03,$3b
  2095.  .byt $03,$3a,$1b,$3b,$8b,$05,$35,$03
  2096.  .byt $33,$07,$32,$03,$30,$03,$2f,$0b
  2097.  .byt $30,$03,$32,$0f,$30,$0b,$35,$03
  2098.  .byt $33,$07,$32,$03,$30,$03,$2f,$1f
  2099.  .byt $30,$8b,$00,$3c,$01,$3c,$01,$3e
  2100.  .byt $03,$3f,$03,$41,$03,$3f,$03,$3e
  2101.  .byt $0b,$3d,$01,$3d,$01,$3f,$03,$40
  2102.  .byt $03,$42,$03,$40,$03,$3f,$03,$3e
  2103.  .byt $01,$3e,$01,$40,$03,$41,$03,$40
  2104.  .byt $03,$3e,$03,$3d,$03,$3e,$03,$3c
  2105.  .byt $03,$3a,$01,$3a,$01,$3c,$03,$3d
  2106.  .byt $03,$3c,$03,$3a,$03,$39,$03,$3a
  2107.  .byt $03,$3c,$ff
  2108.  
  2109. ptn09 =*
  2110.  .byt $83,$00,$32,$01,$35,$01,$34,$03
  2111.  .byt $32,$03,$35,$03,$34,$03,$32,$03
  2112.  .byt $35,$01,$34,$01,$32,$03,$32,$03
  2113.  .byt $3a,$03,$39,$03,$3a,$03,$32,$03
  2114.  .byt $3a,$03,$39,$03,$3a,$ff
  2115.  
  2116. ptn2a =*
  2117.  .byt $03,$34,$01,$37,$01,$35,$03,$34
  2118.  .byt $03,$37,$03,$35,$03,$34,$03,$37
  2119.  .byt $01,$35,$01,$34,$03,$34,$03,$3a
  2120.  .byt $03,$39,$03,$3a,$03,$34,$03,$3a
  2121.  .byt $03,$39,$03,$3a,$ff
  2122.  
  2123. ptn2b =*
  2124.  .byt $03,$39,$03,$38,$03,$39,$03,$3a
  2125.  .byt $03,$39,$03,$37,$03,$35,$03,$34
  2126.  .byt $03,$35,$03,$34,$03,$35,$03,$37
  2127.  .byt $03,$35,$03,$34,$03,$32,$03,$31
  2128.  .byt $ff
  2129.  
  2130. ptn0a =*
  2131.  .byt $03
  2132.  .byt $37,$01,$3a,$01,$39,$03,$37,$03
  2133.  .byt $3a,$03,$39,$03,$37,$03,$3a,$01
  2134.  .byt $39,$01,$37,$03,$37,$03,$3e,$03
  2135.  .byt $3d,$03,$3e,$03,$37,$03,$3e,$03
  2136.  .byt $3d,$03,$3e,$03,$3d,$01,$40,$01
  2137.  .byt $3e,$03,$3d,$03,$40,$01,$3e,$01
  2138.  .byt $3d,$03,$40,$03,$3e,$03,$40,$03
  2139.  .byt $40,$01,$43,$01,$41,$03,$40,$03
  2140.  .byt $43,$01,$41,$01,$40,$03,$43,$03
  2141.  .byt $41,$03,$43,$03,$43,$01,$46,$01
  2142.  .byt $45,$03,$43,$03,$46,$01,$45,$01
  2143.  .byt $43,$03,$46,$03,$45,$03,$43,$01
  2144.  .byt $48,$01,$49,$01,$48,$01,$46,$01
  2145.  .byt $45,$01,$46,$01,$45,$01,$43,$01
  2146.  .byt $41,$01,$43,$01,$41,$01,$40,$01
  2147.  .byt $3d,$01,$39,$01,$3b,$01,$3d,$ff
  2148.  
  2149. ptn0d =*
  2150.  .byt $01,$3e,$01,$39,$01,$35,$01,$39
  2151.  .byt $01,$3e,$01,$39,$01,$35,$01,$39
  2152.  .byt $03,$3e,$01,$41,$01,$40,$03,$40
  2153.  .byt $01,$3d,$01,$3e,$01,$40,$01,$3d
  2154.  .byt $01,$39,$01,$3d,$01,$40,$01,$3d
  2155.  .byt $01,$39,$01,$3d,$03,$40,$01,$43
  2156.  .byt $01,$41,$03,$41,$01,$3e,$01,$40
  2157.  .byt $01,$41,$01,$3e,$01,$39,$01,$3e
  2158.  .byt $01,$41,$01,$3e,$01,$39,$01,$3e
  2159.  .byt $03,$41,$01,$45,$01,$43,$03,$43
  2160.  .byt $01,$40,$01,$41,$01,$43,$01,$40
  2161.  .byt $01,$3d,$01,$40,$01,$43,$01,$40
  2162.  .byt $01,$3d,$01,$40,$01,$46,$01,$43
  2163.  .byt $01,$45,$01,$46,$01,$44,$01,$43
  2164.  .byt $01,$40,$01,$3d,$ff
  2165.  
  2166. ptn0f =*
  2167.  .byt $01,$3e,$01
  2168.  .byt $39,$01,$35,$01,$39,$01,$3e,$01
  2169.  .byt $39,$01,$35,$01,$39,$01,$3e,$01
  2170.  .byt $39,$01,$35,$01,$39,$01,$3e,$01
  2171.  .byt $39,$01,$35,$01,$39,$01,$3e,$01
  2172.  .byt $3a,$01,$37,$01,$3a,$01,$3e,$01
  2173.  .byt $3a,$01,$37,$01,$3a,$01,$3e,$01
  2174.  .byt $3a,$01,$37,$01,$3a,$01,$3e,$01
  2175.  .byt $3a,$01,$37,$01,$3a,$01,$40,$01
  2176.  .byt $3d,$01,$39,$01,$3d,$01,$40,$01
  2177.  .byt $3d,$01,$39,$01,$3d,$01,$40,$01
  2178.  .byt $3d,$01,$39,$01,$3d,$01,$40,$01
  2179.  .byt $3d,$01,$39,$01,$3d,$01,$41,$01
  2180.  .byt $3e,$01,$39,$01,$3e,$01,$41,$01
  2181.  .byt $3e,$01,$39,$01,$3e,$01,$41,$01
  2182.  .byt $3e,$01,$39,$01,$3e,$01,$41,$01
  2183.  .byt $3e,$01,$39,$01,$3e,$01,$43,$01
  2184.  .byt $3e,$01,$3a,$01,$3e,$01,$43,$01
  2185.  .byt $3e,$01,$3a,$01,$3e,$01,$43,$01
  2186.  .byt $3e,$01,$3a,$01,$3e,$01,$43,$01
  2187.  .byt $3e,$01,$3a,$01,$3e,$01,$43,$01
  2188.  .byt $3f,$01,$3c,$01,$3f,$01,$43,$01
  2189.  .byt $3f,$01,$3c,$01,$3f,$01,$43,$01
  2190.  .byt $3f,$01,$3c,$01,$3f,$01,$43,$01
  2191.  .byt $3f,$01,$3c,$01,$3f,$01,$45,$01
  2192.  .byt $42,$01,$3c,$01,$42,$01,$45,$01
  2193.  .byt $42,$01,$3c,$01,$42,$01,$48,$01
  2194.  .byt $45,$01,$42,$01,$45,$01,$4b,$01
  2195.  .byt $48,$01,$45,$01,$48,$01,$4b,$01
  2196.  .byt $4a,$01,$48,$01,$4a,$01,$4b,$01
  2197.  .byt $4a,$01,$48,$01,$4a,$01,$4b,$01
  2198.  .byt $4a,$01,$48,$01,$4a,$01,$4c,$01
  2199.  .byt $4e,$03,$4f,$ff
  2200.  
  2201. ptn11 =*
  2202.  .byt $bf,$06,$56,$1f,$57,$1f,$56,$1f
  2203.  .byt $5b,$1f,$56,$1f,$57,$1f,$56,$1f
  2204.  .byt $4f,$ff
  2205.  
  2206. ptn12 =*
  2207.  .byt $bf,$0c,$68,$7f,$7f,$7f,$7f,$7f
  2208.  .byt $7f,$7f,$ff
  2209.  
  2210. ptn13 =*
  2211.  .byt $bf,$08,$13,$3f,$13,$3f,$13,$3f
  2212.  .byt $13,$3f,$13,$3f,$13,$3f,$13,$1f
  2213.  .byt $13,$ff
  2214.  
  2215. ptn14 =*
  2216.  .byt $97,$09,$2e,$03,$2e,$1b,$32,$03
  2217.  .byt $32,$1b,$31,$03,$31,$1f,$34,$43
  2218.  .byt $17,$32,$03,$32,$1b,$35,$03,$35
  2219.  .byt $1b,$34,$03,$34,$0f,$37,$8f,$0a
  2220.  .byt $37,$43,$ff
  2221.  
  2222. ptn15 =*
  2223.  .byt $97,$09,$2b,$03,$2b,$1b,$2e,$03
  2224.  .byt $2e,$1b,$2d,$03,$2d,$1f,$30,$43
  2225.  .byt $17,$2e,$03,$2e,$1b,$32,$03,$32
  2226.  .byt $1b,$31,$03,$31,$0f,$34,$8f,$0a
  2227.  .byt $34,$43,$ff
  2228.  
  2229. ptn16 =*
  2230.  .byt $0f,$1f,$0f,$1f,$0f,$1f,$0f,$1f
  2231.  .byt $0f,$1f,$0f,$1f,$0f,$1f,$0f,$1f
  2232.  .byt $0f,$1f,$0f,$1f,$0f,$1f,$0f,$1f
  2233.  .byt $0f,$1f,$0f,$1f,$0f,$1f,$0f,$1f
  2234.  .byt $ff
  2235.  
  2236. ptn17 =*
  2237.  .byt $97,$09,$33,$03,$33,$1b,$37,$03
  2238.  .byt $37,$1b,$36,$03,$36,$1f,$39,$43
  2239.  .byt $17,$37,$03,$37,$1b,$3a,$03,$3a
  2240.  .byt $1b,$39,$03,$39,$2f,$3c,$21,$3c
  2241.  .byt $21,$3d,$21,$3e,$21,$3f,$21,$40
  2242.  .byt $21,$41,$21,$42,$21,$43,$21,$44
  2243.  .byt $01,$45,$ff
  2244.  
  2245. ptn18 =*
  2246.  .byt $97,$09,$30,$03,$30,$1b,$33,$03
  2247.  .byt $33,$1b,$32,$03,$32,$1f,$36,$43
  2248.  .byt $17,$33,$03,$33,$1b,$37,$03,$37
  2249.  .byt $1b,$36,$03,$36,$2f,$39,$21,$39
  2250.  .byt $21,$3a,$21,$3b,$21,$3c,$21,$3d
  2251.  .byt $21,$3e,$21,$3f,$21,$40,$21,$41
  2252.  .byt $01,$42,$ff
  2253.  
  2254. ptn19 =*
  2255.  .byt $0f,$1a,$0f,$1a,$0f,$1a,$0f,$1a
  2256.  .byt $0f,$1a,$0f,$1a,$0f,$1a,$0f,$1a
  2257.  .byt $0f,$1a,$0f,$1a,$0f,$1a,$0f,$1a
  2258.  .byt $0f,$1a,$0f,$1a,$0f,$1a,$0f,$1a
  2259.  .byt $ff
  2260.  
  2261. ptn1a =*
  2262.  .byt $1f,$46,$bf,$0a,$46,$7f,$7f,$ff
  2263.  
  2264. ptn1b =*
  2265.  .byt $1f,$43,$bf,$0a,$43,$7f,$ff
  2266.  
  2267. ptn1c =*
  2268.  .byt $83,$02,$13,$03,$13,$03,$1e,$03
  2269.  .byt $1f,$03,$13,$03,$13,$03,$1e,$03
  2270.  .byt $1f,$03,$13,$03,$13,$03,$1e,$03
  2271.  .byt $1f,$03,$13,$03,$13,$03,$1e,$03
  2272.  .byt $1f,$03,$13,$03,$13,$03,$1e,$03
  2273.  .byt $1f,$03,$13,$03,$13,$03,$1e,$03
  2274.  .byt $1f,$03,$13,$03,$13,$03,$1e,$03
  2275.  .byt $1f,$03,$13,$03,$13,$03,$1e,$03
  2276.  .byt $1f,$ff
  2277.  
  2278. ptn29 =*
  2279.  .byt $8f,$0b,$38,$4f,$ff
  2280.  
  2281. ptn2c =*
  2282.  .byt $83,$0e,$32,$07,$32,$07,$2f,$07
  2283.  .byt $2f,$03,$2b,$87,$0b,$46,$83,$0e
  2284.  .byt $2c,$03,$2c,$8f,$0b,$32,$ff
  2285.  
  2286. ptn2d =*
  2287.  .byt $43,$83,$0e,$32,$03,$32,$03,$2f
  2288.  .byt $03,$2f,$03,$2c,$87,$0b,$38,$ff
  2289.  
  2290. ptn39 =*
  2291.  .byt $83,$01
  2292.  .byt $43,$01,$4f,$01,$5b,$87,$03,$2f
  2293.  .byt $83,$01,$43,$01,$4f,$01,$5b,$87
  2294.  .byt $03,$2f,$83,$01,$43,$01,$4f,$01
  2295.  .byt $5b,$87,$03,$2f,$83,$01,$43,$01
  2296.  .byt $4f,$01,$5b,$87,$03,$2f,$83,$01
  2297.  .byt $43,$01,$4f,$01,$5b,$87,$03,$2f
  2298.  .byt $83,$01,$43,$01,$4f,$01,$5b,$87
  2299.  .byt $03,$2f
  2300.  
  2301. ptn01 =*
  2302.  .byt $83,$01,$43,$01,$4f,$01,$5b,$87
  2303.  .byt $03,$2f,$83,$01,$43,$01,$4f,$01
  2304.  .byt $5b,$87,$03,$2f,$ff
  2305.  
  2306. ptn02 =*
  2307.  .byt $83,$02,$13,$03,$13,$03,$1f,$03
  2308.  .byt $1f,$03,$13,$03,$13,$03,$1f,$03
  2309.  .byt $1f,$ff
  2310.  
  2311. ptn1d =*
  2312.  .byt $03,$15,$03,$15,$03,$1f,$03,$21
  2313.  .byt $03,$15,$03,$15,$03,$1f,$03,$21
  2314.  .byt $ff
  2315.  
  2316. ptn1e =*
  2317.  .byt $03,$1a,$03,$1a,$03,$1c,$03,$1c
  2318.  .byt $03,$1d,$03,$1d,$03,$1e,$03,$1e
  2319.  .byt $ff
  2320.  
  2321. ptn1f =*
  2322.  .byt $03,$1a,$03,$1a,$03,$24,$03,$26
  2323.  .byt $03,$13,$03,$13,$07,$1f,$ff
  2324.  
  2325. ptn04 =*
  2326.  .byt $03,$18,$03,$18,$03,$24,$03,$24
  2327.  .byt $03,$18,$03,$18,$03,$24,$03,$24
  2328.  .byt $03,$20,$03,$20,$03,$2c,$03,$2c
  2329.  .byt $03,$20,$03,$20,$03,$2c,$03,$2c
  2330.  .byt $ff
  2331.  
  2332. ptn20 =*
  2333.  .byt $03,$19,$03,$19,$03
  2334.  .byt $25,$03,$25,$03,$19,$03,$19,$03
  2335.  .byt $25,$03,$25,$03,$21,$03,$21,$03
  2336.  .byt $2d,$03,$2d,$03,$21,$03,$21,$03
  2337.  .byt $2d,$03,$2d,$ff
  2338.  
  2339. ptn06 =*
  2340.  .byt $03,$1a,$03,$1a
  2341.  .byt $03,$26,$03,$26,$03,$1a,$03,$1a
  2342.  .byt $03,$26,$03,$26,$03,$15,$03,$15
  2343.  .byt $03,$21,$03,$21,$03,$15,$03,$15
  2344.  .byt $03,$21,$03,$21,$03,$18,$03,$18
  2345.  .byt $03,$24,$03,$24,$03,$18,$03,$18
  2346.  .byt $03,$24,$03,$24,$03,$1f,$03,$1f
  2347.  .byt $03,$2b,$03,$2b,$03,$1f,$03,$1f
  2348.  .byt $03,$2b,$03,$2b,$03,$1a,$03,$1a
  2349.  .byt $03,$26,$03,$26,$03,$1a,$03,$1a
  2350.  .byt $03,$26,$03,$26,$03,$15,$03,$15
  2351.  .byt $03,$21,$03,$21,$03,$15,$03,$15
  2352.  .byt $03,$21,$03,$21,$03,$18,$03,$18
  2353.  .byt $03,$24,$03,$24,$03,$18,$03,$18
  2354.  .byt $03,$24,$03,$24,$03,$1c,$03,$1c
  2355.  .byt $03,$28,$03,$28,$03,$1c,$03,$1c
  2356.  .byt $03,$28,$03,$28
  2357.  
  2358. ptn3b =*
  2359.  .byt $83,$04,$36,$07
  2360.  .byt $36,$07,$37,$07,$36,$03,$33,$07
  2361.  .byt $32,$57,$ff
  2362.  
  2363. ptn08 =*
  2364.  .byt $83,$02,$1b,$03,$1b,$03,$27,$03
  2365.  .byt $27,$03,$1b,$03,$1b,$03,$27,$03
  2366.  .byt $27,$ff
  2367.  
  2368. ptn21 =*
  2369.  .byt $03,$1c,$03,$1c,$03,$28,$03,$28
  2370.  .byt $03,$1c,$03,$1c,$03,$28,$03,$28
  2371.  .byt $ff
  2372.  
  2373. ptn22 =*
  2374.  .byt $03,$1d,$03,$1d,$03,$29,$03,$29
  2375.  .byt $03,$1d,$03,$1d,$03,$29,$03,$29
  2376.  .byt $ff
  2377.  
  2378. ptn23 =*
  2379.  .byt $03,$18,$03,$18,$03,$24,$03,$24
  2380.  .byt $03,$18,$03,$18,$03,$24,$03,$24
  2381.  .byt $ff
  2382.  
  2383. ptn24 =*
  2384.  .byt $03,$1e,$03,$1e,$03,$2a,$03,$2a
  2385.  .byt $03,$1e,$03,$1e,$03,$2a,$03,$2a
  2386.  .byt $ff
  2387.  
  2388. ptn25 =*
  2389.  .byt $83,$05,$26,$01,$4a,$01,$34,$03
  2390.  .byt $29,$03,$4c,$03,$4a,$03,$31,$03
  2391.  .byt $4a,$03,$24,$03,$22,$01,$46,$01
  2392.  .byt $30,$03,$25,$03,$48,$03,$46,$03
  2393.  .byt $2d,$03,$46,$03,$24,$ff
  2394.  
  2395. ptn0b =*
  2396.  .byt $83,$02,$1a,$03,$1a,$03,$26,$03
  2397.  .byt $26,$03,$1a,$03,$1a,$03,$26,$03
  2398.  .byt $26,$ff
  2399.  
  2400. ptn0c =*
  2401.  .byt $03,$13,$03,$13,$03,$1d,$03,$1f
  2402.  .byt $03,$13,$03,$13,$03,$1d,$03,$1f
  2403.  .byt $ff
  2404.  
  2405. ptn26 =*
  2406.  .byt $87,$02,$1a,$87,$03,$2f,$83,$02
  2407.  .byt $26,$03,$26,$87,$03,$2f,$ff
  2408.  
  2409. ptn10 =*
  2410.  .byt $07,$1a,$4f,$47,$ff
  2411.  
  2412. ptn0e =*
  2413.  .byt $03,$1f,$03,$1f,$03,$24,$03,$26
  2414.  .byt $07,$13,$47,$ff
  2415.  
  2416. ptn30 =*
  2417.  .byt $bf,$0f,$32,$0f,$32,$8f,$90,$30
  2418.  .byt $3f,$32,$13,$32,$03,$32,$03,$35
  2419.  .byt $03,$37,$3f,$37,$0f,$37,$8f,$90
  2420.  .byt $30,$3f,$32,$13,$32,$03,$2d,$03
  2421.  .byt $30,$03,$32,$ff
  2422.  
  2423. ptn31 =*
  2424.  .byt $0f,$32
  2425.  .byt $af,$90,$35,$0f,$37,$a7,$99,$37
  2426.  .byt $07,$35,$3f,$32,$13,$32,$03,$32
  2427.  .byt $a3,$e8,$35,$03,$37,$0f,$35,$af
  2428.  .byt $90,$37,$0f,$37,$a7,$99,$37,$07
  2429.  .byt $35,$3f,$32,$13,$32,$03,$2d,$a3
  2430.  .byt $e8,$30,$03,$32,$ff
  2431.  
  2432. ptn32 =*
  2433.  .byt $07,$32,$03
  2434.  .byt $39,$13,$3c,$a7,$9a,$37,$a7,$9b
  2435.  .byt $38,$07,$37,$03,$35,$03,$32,$03
  2436.  .byt $39,$1b,$3c,$a7,$9a,$37,$a7,$9b
  2437.  .byt $38,$07,$37,$03,$35,$03,$32,$03
  2438.  .byt $39,$03,$3c,$03,$3e,$03,$3c,$07
  2439.  .byt $3e,$03,$3c,$03,$39,$a7,$9a,$37
  2440.  .byt $a7,$9b,$38,$07,$37,$03,$35,$03
  2441.  .byt $32,$af,$90,$3c,$1f,$3e,$43,$03
  2442.  .byt $3e,$03,$3c,$03,$3e,$ff
  2443.  
  2444. ptn33 =*
  2445.  .byt $03,$3e
  2446.  .byt $03,$3e,$a3,$e8,$3c,$03,$3e,$03
  2447.  .byt $3e,$03,$3e,$a3,$e8,$3c,$03,$3e
  2448.  .byt $03,$3e,$03,$3e,$a3,$e8,$3c,$03
  2449.  .byt $3e,$03,$3e,$03,$3e,$a3,$e8,$3c
  2450.  .byt $03,$3e,$af,$91,$43,$1f,$41,$43
  2451.  .byt $03,$3e,$03,$41,$03,$43,$03,$43
  2452.  .byt $03,$43,$a3,$e8,$41,$03,$43,$03
  2453.  .byt $43,$03,$43,$a3,$e8,$41,$03,$43
  2454.  .byt $03,$45,$03,$48,$a3,$fd,$45,$03
  2455.  .byt $44,$01,$43,$01,$41,$03,$3e,$03
  2456.  .byt $3c,$03,$3e,$2f,$3e,$bf,$98,$3e
  2457.  .byt $43,$03,$3e,$03,$3c,$03,$3e,$ff
  2458.  
  2459. ptn34 =*
  2460.  .byt $03,$4a,$03,$4a,$a3,$f8,$48,$03
  2461.  .byt $4a,$03,$4a,$03,$4a,$a3,$f8,$48
  2462.  .byt $03,$4a,$ff
  2463.  
  2464. ptn35 =*
  2465.  .byt $01,$51,$01,$54,$01
  2466.  .byt $51,$01,$54,$01,$51,$01,$54,$01
  2467.  .byt $51,$01,$54,$01,$51,$01,$54,$01
  2468.  .byt $51,$01,$54,$01,$51,$01,$54,$01
  2469.  .byt $51,$01,$54,$ff
  2470.  
  2471. ptn36 =*
  2472.  .byt $01,$50,$01,$4f
  2473.  .byt $01,$4d,$01,$4a,$01,$4f,$01,$4d
  2474.  .byt $01,$4a,$01,$48,$01,$4a,$01,$48
  2475.  .byt $01,$45,$01,$43,$01,$44,$01,$43
  2476.  .byt $01,$41,$01,$3e,$01,$43,$01,$41
  2477.  .byt $01,$3e,$01,$3c,$01,$3e,$01,$3c
  2478.  .byt $01,$39,$01,$37,$01,$38,$01,$37
  2479.  .byt $01,$35,$01,$32,$01,$37,$01,$35
  2480.  .byt $01,$32,$01,$30,$ff
  2481.  
  2482. ptn37 =*
  2483.  .byt $5f,$5f,$5f
  2484.  .byt $47,$83,$0e,$32,$07,$32,$07,$2f
  2485.  .byt $03,$2f,$07,$2f,$97,$0b,$3a,$5f
  2486.  .byt $5f,$47,$8b,$0e,$32,$03,$32,$03
  2487.  .byt $2f,$03,$2f,$47,$97,$0b,$3a,$5f
  2488.  .byt $5f,$47,$83,$0e,$2f,$0b,$2f,$03
  2489.  .byt $2f,$03,$2f,$87,$0b,$30,$17,$3a
  2490.  .byt $5f,$8b,$0e,$32,$0b,$32,$0b,$2f
  2491.  .byt $0b,$2f,$07,$2c,$07,$2c,$ff
  2492.  
  2493. ptn38 =*
  2494.  .byt $87
  2495.  .byt $0b,$34,$17,$3a,$5f,$5f,$84,$0e
  2496.  .byt $32,$04,$32,$05,$32,$04,$2f,$04
  2497.  .byt $2f,$05,$2f,$47,$97,$0b,$3a,$5f
  2498.  .byt $5f,$84,$0e,$32,$04,$32,$05,$32
  2499.  .byt $04,$2f,$04,$2f,$05,$2f,$ff
  2500.  
  2501. ptn2f =*
  2502.  .byt $03,$1a,$03,$1a,$03
  2503.  .byt $24,$03,$26,$03,$1a,$03,$1a,$03
  2504.  .byt $18,$03,$19,$03,$1a,$03,$1a,$03
  2505.  .byt $24,$03,$26,$03,$1a,$03,$1a,$03
  2506.  .byt $18,$03,$19,$03,$18,$03,$18,$03
  2507.  .byt $22,$03,$24,$03,$18,$03,$18,$03
  2508.  .byt $16,$03,$17,$03,$18,$03,$18,$03
  2509.  .byt $22,$03,$24,$03,$18,$03,$18,$03
  2510.  .byt $16,$03,$17,$03,$13,$03,$13,$03
  2511.  .byt $1d,$03,$1f,$03,$13,$03,$13,$03
  2512.  .byt $1d,$03,$1e,$03,$13,$03,$13,$03
  2513.  .byt $1d,$03,$1f,$03,$13,$03,$13,$03
  2514.  .byt $1d,$03,$1e,$03,$1a,$03,$1a,$03
  2515.  .byt $24,$03,$26,$03,$1a,$03,$1a,$03
  2516.  .byt $18,$03,$19,$03,$1a,$03,$1a,$03
  2517.  .byt $24,$03,$26,$03,$1a,$03,$1a,$03
  2518.  .byt $18,$03,$19,$ff
  2519.  
  2520.  
  2521. ;====================================
  2522. ;instruments
  2523. ;====================================
  2524.  
  2525. instr =*
  2526.  .byt $80,$09,$41,$48,$60,$03,$81,$00
  2527.  .byt $00,$08,$81,$02,$08,$00,$00,$01
  2528.  .byt $a0,$02,$41,$09,$80,$00,$00,$00
  2529.  .byt $00,$02,$81,$09,$09,$00,$00,$05
  2530.  .byt $00,$08,$41,$08,$50,$02,$00,$04
  2531.  .byt $00,$01,$41,$3f,$c0,$02,$00,$00
  2532.  .byt $00,$08,$41,$04,$40,$02,$00,$00
  2533.  .byt $00,$08,$41,$09,$00,$02,$00,$00
  2534.  .byt $00,$09,$41,$09,$70,$02,$5f,$04
  2535.  .byt $00,$09,$41,$4a,$69,$02,$81,$00
  2536.  .byt $00,$09,$41,$40,$6f,$00,$81,$02
  2537.  .byt $80,$07,$81,$0a,$0a,$00,$00,$01
  2538.  .byt $00,$09,$41,$3f,$ff,$01,$e7,$02
  2539.  .byt $00,$08,$41,$90,$f0,$01,$e8,$02
  2540.  .byt $00,$08,$41,$06,$0a,$00,$00,$01
  2541.  .byt $00,$09,$41,$19,$70,$02,$a8,$00
  2542.  .byt $00,$02,$41,$09,$90,$02,$00,$00
  2543.  .byt $00,$00,$11,$0a,$fa,$00,$00,$05
  2544.  .byt $00,$08,$41,$37,$40,$02,$00,$00
  2545.  .byt $00,$08,$11,$07,$70,$02,$00,$00
  2546.  
  2547. .end
  2548.  
  2549. =============================================================================
  2550. ZPM3 and ZCCP Enhancements for CP/M Plus from Simeon Cran
  2551. by Randy Winchester (randy@mit.edu)
  2552.  
  2553. Operating System Components
  2554.  
  2555. The CP/M Plus operating system consists of three modules.  The CCP (Console
  2556. Command Processor), is the part of CP/M that you see when you first boot the
  2557. system.  The CCP prints the A> disk prompt, accepts user input, and loads
  2558. commands from disk.
  2559.  
  2560. The BDOS (Basic Disk Operating System) handles the CP/M functions of disk,
  2561. console, and printer input/output, and the tasks of file management.
  2562.  
  2563. The BIOS (Basic Input Output System) does the real input/output work for the
  2564. BDOS. The BIOS contains the code customized for the CP/M hardware that you're
  2565. using. On the C128, the BIOS contains the routines for driving the 40 and 80
  2566. column screens, using the REU as a RAM drive, and reading/writing several
  2567. different disk formats on 1571 and 1581 drives.  The BIOS can be thought of as
  2568. a collection of device drivers that are specific to your computer.
  2569.  
  2570.  
  2571. What's New - BIOS-R6
  2572.  
  2573. BIOS-R6 (C128 BIOS modified by Randy Winchester and others) is the latest of
  2574. the modified versions of the C128 CP/M BIOS.  Most of the changes to the BIOS
  2575. result in faster processing speed. For example, all the code for driving a 40
  2576. column screen has been removed.  Almost everyone using CP/M is going to be
  2577. using it in 80 columns anyway.  Cutting this code takes a big load off the
  2578. system and increases overall speed by about 15%.  Similarly, the interrupt
  2579. driven RS232 has been set from 300 to 75 baud.  The higher the baud rate, the
  2580. more processor time is required to service RS232.  Since the RS232 code is
  2581. always running, decreasing the baud rate frees up cycles that the processor
  2582. needs to service RS232.  This doesn't affect the operation of terminal programs
  2583. which explicitly set the baud rate when they start up.
  2584.  
  2585. Other features of BIOS-R6 include a screen dump function, commented source to
  2586. assist the programmer in producing customized systems, and support for
  2587. additional disk formats.  Some of the new disk formats include Commodore's
  2588. standard 1581 CP/M format, MAXI 71 (398K on 5.25" disks), and GP 1581 (796K on
  2589. 3.5" disks).
  2590.  
  2591. C128 CP/M programmers who want to add or change operating system features
  2592. should try to make changes to the BIOS.  For one thing, BIOS source code is
  2593. available, but not available for the BDOS or CCP.  (Source code is not
  2594. available for the BDOS and CCP replacements mentioned in this article either). 
  2595. Another reason is that the BDOS and CCP are intended to be "invariable"
  2596. operating system components - that is, they are identical for different
  2597. computers that run CP/M Plus.  A study of the BIOS source code will reveal
  2598. segments of code that can be removed if they aren't needed, and will provide
  2599. hints as to new features that can be added.
  2600.  
  2601. The distribution package, BIOS-R6.LBR includes documentation, source code,
  2602. utilities, and support files.  BIOS-R6.LBR also contains the latest version of
  2603. ZPM3. [Ed. Note: The files mentioned in this article can be found via
  2604. anonymous FTP or via the mailserver through the "psend" command.]
  2605.  
  2606.  
  2607. ZPM3 Features
  2608.  
  2609. ZPM3 is a replacement BDOS by Simeon Cran.  Since the BDOS is supposed to be
  2610. "invariable," why would anyone want to replace it? The answers to that are
  2611. pretty typical - bug fixes, speed enhancements, and new features!  ZPM3
  2612. interacts with the BIOS and CCP in most of the same ways as the standard
  2613. Digital Research BDOS, and for the most part appears to be a clone of the
  2614. standard BDOS.  The standard BDOS was coded in 8080 assembly to make it
  2615. compatible with machines that use the older slower 8080 processor.  Very few
  2616. (if any) CP/M Plus machines used the 8080. ZPM3 is coded in faster, compact Z80
  2617. assembly language, for the Z80 processor that is at the heart of most CP/M Plus
  2618. computers (including the C128).
  2619.  
  2620. The ZPM3 documentation details fixes to several bugs that have plagued CP/M
  2621. Plus since day one.  Although the bugs sound somewhat obscure, there's no
  2622. telling when one might cause problems.
  2623.  
  2624. ZPM3 is much faster than standard CP/M Plus.  The increased speed should be
  2625. obvious after using it for a short time.
  2626.  
  2627. The new features offered by ZPM3 are remarkable.  Three closely related
  2628. features are enhanced command line editing, a history buffer that stores and
  2629. recalls multiple commands, and Automatic Command Prompting.  These features
  2630. work in concert to provide a flexible and convenient command line interface. 
  2631. Command line editing now has 20 control key functions for moving or deleting by
  2632. characters or whole words.  The most recent command lines (up to 250
  2633. characters) are stored in the history buffer, and can be recalled and reused,
  2634. or reedited if necessary.  Automatic Command Prompting is best appreciated if
  2635. seen in action.  It's similar to command line completion in Unix, except that
  2636. it's automatic, with matching responses coming directly from the history
  2637. buffer.  If you've recently entered a long command line with lots of options,
  2638. and need to reuse it (or edit it slightly first), typing the first few unique
  2639. characters will bring back the entire command from the history buffer if it's
  2640. still intact.  Automatic Command Prompting is so radical that it might take
  2641. some getting used to. If you don't think you can get used to it, it can be shut
  2642. off.
  2643.  
  2644. The latest version of ZPM3, ZPM3N08.ARK, is included inside BIOS-R6.LBR, and
  2645. can also be found as a separate file.
  2646.  
  2647.  
  2648.  
  2649. ZCCP Documentation, Version 1.0
  2650.  
  2651. The remainder of this article will describe ZCCP and how to configure a system
  2652. disk to get a fully functional ZPM3/ZCCP system up and running.  BIOS-R6 and
  2653. ZPM3 both come with enough documentation to keep you busy for hours, but ZCCP
  2654. has never been distributed by itself, because up until this article, there has
  2655. not been any documentation for it.  Most of the documentation that follows was
  2656. figured out through experimentation and later verified by Simeon Cran.
  2657.  
  2658. ZCCP Features
  2659.  
  2660. This documentation is provided to assist the user in getting a ZCCP system up
  2661. and running.  It is not an exhaustive course on Z- System or ZCPR.  The
  2662. following list details which ZCPR features are provided with ZCCP, and which
  2663. ones aren't.
  2664.  
  2665.     * ZCPR 3.3 compatibility.  ZCCP can run a wide range of utilities an
  2666.     applications created for ZCPR 3.3 and ZCPR 3.4.
  2667.  
  2668.     * TCAP.  A Z3T termcap file describing terminal characteristics can be
  2669.     loaded into the system.  Z-System programs make use of the TCAP for output
  2670.     to the screen - a big improvement over the old method of patching
  2671.     individual programs with terminal control codes.  TCAP files are loaded by
  2672.     the ZCCP LOADSEG command.
  2673.  
  2674.     * Named directories.  User areas can be assigned names.  Up to 12 user
  2675.     areas can be assigned names.  Named Directory Registers (*.NDR files) are
  2676.     loaded by the ZCCP LOADSEG command.
  2677.  
  2678.     * Command Search Path.  ZCCP will search for commands along a user defined
  2679.     search path.  Up to six path elements (directories) can be defined.
  2680.  
  2681.     * Environment block.  Contains TCAP, Named Directory, and Path information. 
  2682.     Also includes a map of active disk drives and other system information. 
  2683.     The environment block can be viewed with the Z-System SHOW utility.
  2684.  
  2685.     * Flow control.  Conditional processing for batch files.  Relies on
  2686.     Z-System IF.COM for setting the flow state.  Other flow control commands
  2687.     (FI, ELSE, XIF, OR, AND) are resident.
  2688.  
  2689.     * Multiple commands can be entered on the command line.  The command line
  2690.     buffer will hold up to 225 characters.  Commands should be separated by
  2691.     semicolons.
  2692.  
  2693.     * Extended Command Processor.  If a command is not a built-in flow command,
  2694.     resident command, or located on disk along the search path, the command
  2695.     line is passed to an extended command processor.  A typical extended
  2696.     command processor is ARUNZ, a sophisticated batch file executor with alias
  2697.     features.  To use a program as an extended command processor, rename it to
  2698.     CMDRUN.COM and place it in the ROOT directory of your boot disk.
  2699.  
  2700.     * Error handler.  In the event that the extended command processor can't
  2701.     handle a command, control is passed to an error handler.  Error handlers
  2702.     give information about the error (instead of the useless CP/M "?" message)
  2703.     and allow the command line to be edited and reused.
  2704.  
  2705.     * Resident commands.  The following commands are built in: 
  2706.       CLS  - clears the screen 
  2707.       NOTE - text following the NOTE command is treated as a comment.
  2708.       FI   - Flow control:  terminate the current 
  2709.       IF level ELSE - Flow control:  toggle the flow state
  2710.       XIF  - Flow control:  exit all pending IF levels 
  2711.       OR   - Flow control:  OR IF tests to set flow state 
  2712.       AND  - Flow control:  AND IF tests to set flow state
  2713.  
  2714.     * Shell stack.  Up to four shell levels can be defined.  Z-System provides
  2715.     a choice of several different shells.  Applications such as terminal
  2716.     programs and word processors can also be assigned shell status.
  2717.  
  2718.     * ZCCP uses the LOADSEG command for direct loading of RSX files that have
  2719.     not been GENCOMed.  Example: LOADSEG SAVE.RSX loads SAVE.RSX.
  2720.  
  2721.     There are some things that Z3Plus will do that ZCCP won't do.
  2722.  
  2723.     - ZCCP does not support a Flow Command Package (FCP).  It relies on the
  2724.     transient IF command.  Other flow commands (FI, ELSE, XIF, OR, AND) are
  2725.     resident in ZCCP.
  2726.  
  2727.     - A Resident Command Package (RCP) is not implemented.  CLS and NOTE are
  2728.     resident in ZCCP.  All other commands must be loaded from disk.  This isn't
  2729.     as much of a handicap as it might sound if you have a fast RAM drive, such
  2730.     as a CBM 17xx REU, Quick Brown Box, or RAMLink.
  2731.  
  2732.     - ZCCP can not load type 4 programs (used with ZCPR 3.4).  It loads
  2733.     standard COM files at 100H, and type 3 programs that load higher in memory. 
  2734.     Most type 4 programs have type 3 or COM equivalents.
  2735.  
  2736.     - ZCCP can not reexecute loaded programs.  This trick is usually performed
  2737.     on Z-Systems with a GO command that jumps to 100H. Since ZCCP also loads at
  2738.     100H, a GO command would only restart ZCCP.
  2739.  
  2740.  
  2741. The Files
  2742.  
  2743. Three files are included in ZCCP.ARK:
  2744.  
  2745.  File name      Size  Description
  2746.  ============   ====  ==========================================
  2747.  CCP     .COM   3k    ZCCP replacement for CCP.COM
  2748.  LOADSEG .COM   3k    Loader for named directories and termcaps
  2749.  ZINSTAL .ZPM   1k    Segment containing environment information
  2750.  
  2751.  
  2752. Getting Started - Preparing a Boot Disk
  2753.  
  2754. Format a Commodore CP/M format 5.25 or 3.5 inch disk.  ZCCP must be booted from
  2755. device 8 (CP/M drive A).
  2756.  
  2757. Copy the files from ZCCP.ARK to user area 0 of the newly formatted disk.
  2758.  
  2759. Copy CPM+.SYS to user 0 of the boot disk.  The CPM+.SYS must have been
  2760. generated using the BDOS segments from ZPM3.
  2761.  
  2762. Locate a copy of a Z-System alias utility.  A good one is SALIAS16, although
  2763. others should work also.  Copy it to user 0 of the boot disk.
  2764.  
  2765. At this point, hit the reset switch and boot the system with the new disk. 
  2766. After the system boots, you won't be able to do much with it.  The only
  2767. resident commands are CLS and NOTE, and ZCCP can only locate commands if they
  2768. are prefixed with the drive and user number.
  2769.  
  2770. The next step is to create a startup alias.  When ZCCP boots, it looks for a
  2771. file named STARTZPM.COM and executes commands from it.  STARTZPM.COM is created
  2772. with a ZCPR alias utility.  Here is a listing of a STARTZPM.COM created with
  2773. SALIAS:
  2774.  
  2775.      =============================================================
  2776.  
  2777.      A0>SALIAS STARTZPM
  2778.  
  2779.      15:                ; Logs the ROOT directory (A15) on the
  2780.                         ; current drive.
  2781.  
  2782.      QD F/F             ; Installs Quick Brown Box ramdisk driver.
  2783.  
  2784.      LOADSEG NAMES.NDR C128-XBR.Z3T
  2785.                         ; LOADSEG loads the Named Directory Register
  2786.                         ; and TCAP.
  2787.                         ; Directories can now be referred to by
  2788.                         ; name, as in the next command:
  2789.  
  2790.      SETPTH10 /C COMMANDS REU 1581 $$$$ $$0 ROOT
  2791.                         ; SETPTH sets the command search path.
  2792.                         ; The /c option first clears any existing path.
  2793.                         ; Directories are then listed in the
  2794.                         ; order searched.  In this case, COMMANDS
  2795.                         ; is a 64K QBB ramdisk (drive/user F0) where
  2796.                         ; frequently used commands are stored.  REU is
  2797.                         ; a 1750 REU (drive/user M0).  1581 is a 1581
  2798.                         ; drive, (drive/user C15) where some 700K
  2799.                         ; of utilities and applications are
  2800.                         ; located.  $$$$ refers to the currently
  2801.                         ; logged drive and user area.  $$0 refers
  2802.                         ; to user area 0 of the current drive.
  2803.                         ; The ROOT directory is on drive A, user
  2804.                         ; 15, where startup utilities and system
  2805.                         ; files can be found.
  2806.  
  2807.      1571 [AB           ; This speeds up 1571 disk drives A and B
  2808.                         ; by shutting off the redundant write verify.
  2809.  
  2810.      AUTOTOG ON         ; Turns on keyboard control of ZPM3 Auto
  2811.                         ; Command Prompting.  Auto Command
  2812.                         ; Prompting is toggled by entering CTRL-Q.
  2813.  
  2814.      COMMANDS:          ; Logs the commands directory.
  2815.  
  2816.      IF ~EXIST CP.*     ; Test to see if commands are loaded.
  2817.                         ; This line reads:  "If the CP command
  2818.                         ; does not exist . . ." and sets the flow
  2819.                         ; state to true if the file doesn't exist.
  2820.         QD I/F          ; ". . . then initialize the QBB . . ."
  2821.         C1:CP C1:*.* F0:
  2822.                         ; ". . . copy all of the commands in
  2823.                         ; drive/user C1 to the commands (F0)
  2824.                         ; directory . . ."
  2825.      FI                 ; ". . . end if."
  2826.  
  2827.      ROOT:              ; Log the root directory (A15).
  2828.  
  2829.      CP C:ZF*.* M0:     ; Copy ZFILER.COM and ZFILER.CMD to the
  2830.                         ; REU directory (M0).
  2831.  
  2832.      VERROR             ; Install VERROR error handler.
  2833.  
  2834.      DATE S             ; Set the system time and date.
  2835.  
  2836.      ZF                 ; Invoke ZFILER as a shell.
  2837.  
  2838.      =============================================================
  2839.  
  2840. Of course, your STARTZPM alias will vary depending on the hardware you need to
  2841. support, your software preferences, and your work habits.  This alias is close
  2842. to the upward size limit that ZCCP can handle based on the capacity of the
  2843. multiple command buffer.  At the very least, I recommend an alias that will set
  2844. up a search path and load a TCAP.
  2845.  
  2846. Actually, I put the cart before the horse in this example.  If you try to
  2847. reboot your system with the LOADSEG command as listed, you'll notice that you
  2848. don't have a NAMES.NDR file.  There isn't one distributed with ZCCP either. 
  2849. Z-System utilities won't let you edit the NDR either, since the buffer for it
  2850. hasn't been created yet.  This turned out to be a nasty chicken/egg situation,
  2851. hopefully solved by the inclusion of a sample NAMES.NDR file containing simply
  2852. A0:SYSTEM and A15:ROOT.
  2853.  
  2854. At this point, you should have a mostly functioning ZCCP system disk.  Press
  2855. reset and boot it up.  You might want to correct any problems with it or tweak
  2856. it to perfection before moving on.
  2857.  
  2858.  
  2859. List of Z-System Utilities for ZCCP
  2860.  
  2861. Some of the following utilities are essential, others are nice to have.  The
  2862. version numbers listed are the latest known versions at the time that this
  2863. documentation was written.  Utilities can be found on ZNode BBSs, and some of
  2864. them are available on Simtel20 or its mirror sites.  Some of the more important
  2865. utilities will be uploaded to cco.caltech.edu.
  2866.  
  2867.          SALIAS16  - already mentioned in the example above.  SALIAS (or one of
  2868.          the other ZCPR alias utilities) are essential.
  2869.  
  2870.          ARCOPY    - not a ZCPR utility, but one of the best CP/M file copiers
  2871.          ever.
  2872.  
  2873.          SD138B    - excellent DIRectory utility.  SD offers many different
  2874.          types of sorts, list formats, etc., displays date stamps, and supports
  2875.          output to a file.
  2876.  
  2877.          MKDIR32   - utility for manipulating directory names and Named
  2878.          Directory Register (*.NDR) files.
  2879.  
  2880.          ERASE57   - erases files.
  2881.  
  2882.          ZFILER10  - a file management shell that can launch applications. It
  2883.          is programmable in that it can execute user defined macros from a
  2884.          file.  Multiple files can be "tagged" and operated on by other
  2885.          programs.  ZFILER is an excellent program, sort of a GUI desktop
  2886.          without the slow graphics.
  2887.  
  2888.          C128-XGR  - a library of eXtended GRaphics termcaps for the C128. This
  2889.          file is essential if you want to use any ZCPR programs that need a
  2890.          TCAP.  These termcaps are the first for the C128 that implement
  2891.          character graphics, standout mode, and control of blinking reverse,
  2892.          and underline modes.
  2893.  
  2894.          SETPTH10  - used to set the command search path.  Essential!
  2895.  
  2896.          VERROR17  - error handler that displays the command line for
  2897.          reediting.  VERROR17 is the only error handler that I found that works
  2898.          with ZCCP.
  2899.  
  2900.          ZEX50     - Z-System EXecutive is a powerful batch file processor that
  2901.          replaces the CP/M SUBMIT command.
  2902.  
  2903.          LBRHLP22  - Z-System Help utility displays help files.  Help files can
  2904.          be crunched (*.HZP), and/or loaded from a HELP.LBR library.
  2905.  
  2906.          ARUNZ09   - runs an alias script from a text file.  ARUNZ is
  2907.          frequently used as an extended command processor.  To use ARUNZ (or
  2908.          any other executable utility) as an extended command processor, rename
  2909.          it to CMDRUN.COM.
  2910.  
  2911.          VLU102    - Video Library Utility views or extracts files from
  2912.          libraries.  Versions of VLU above 1.02 do not work reliably with
  2913.          ZPM3/ZCCP.
  2914.  
  2915.          Z33IF16   - is the IF.COM discussed in the section on flow control.
  2916.  
  2917.          SHOW14    - displays an immense amount of information about your
  2918.          Z-System.  SHOW also includes a memory patching function.
  2919.  
  2920.          ZCNFG24   - configures Z-System program options.  Most Z-System
  2921.          programs are distributed with a configuration (*.CFG) file that
  2922.          produces a menu of configuration options when run with ZCNFG.
  2923.  
  2924.          ZP17      - Z-System Patch utility edits files, disk sectors, or
  2925.          memory, and includes a built-in RPN calculator and number base
  2926.          converter.
  2927.  
  2928.          ZMAN-NEW  - This is a manual describing Z-System features in depth. 
  2929.          It is based on earlier versions of Z-System, and is a little dated,
  2930.          but otherwise contains information that you won't find anywhere else. 
  2931.          Not everything in the manual applies to operation of ZPM3/ZCCP, but
  2932.          with the documentation presented here, you should be able to get a
  2933.          good idea of what works and what doesn't.
  2934.  
  2935.  
  2936. ZCCP Technical Notes
  2937.  
  2938. ZCCP is a replacement CCP that implements ZCPR 3.3.  It loads at 100H and is
  2939. stored in the bank 0 CCP buffer for fast reloading as does the standard CCP. 
  2940. By contrast, Z3Plus loads into high memory and can be overwritten by transient
  2941. commands, requiring reloading Z3Plus from disk.  Because ZCCP replaces the CCP,
  2942. a ZCCP system has more TPA (transient program area) than a Z3Plus system.  A
  2943. ZCCP system on the C128 has more than 57K of TPA, almost the same amount as a
  2944. standard C128 CP/M system.
  2945.          
  2946. This should be enough information to get started with ZPM3/ZCCP. Set up a boot
  2947. disk, experiment with some Z-System utilities, read ZMAN-NEW, and get some
  2948. applications running.  You'll agree that ZPM3/ZCCP breaths new life into CP/M.
  2949.  
  2950. =============================================================================
  2951. Multi-Tasking on the C=128 - Part 1
  2952. by Craig Taylor (duck@pembvax1.pembroke.edu)
  2953.  
  2954. I.    Introduction / Package Over-view..
  2955.  
  2956.  This article will detail the multi-tasking kernal which I have written butt
  2957.  is still in the debugging stage . The documentation is being released now as
  2958.  C= Hacking has been delayed for a month while this article and a few others
  2959.  were in the process of being shaped. The source code listings, binaries, and
  2960.  a few sample programs will be in the next issue of C= Hacking as well as
  2961.  available on the mailserver and on R. Knop's FTP site when they are
  2962.  available..
  2963.  
  2964.  The Commodore 128 does not support TRUE multi-tasking in that the processor
  2965.  handles swapping from task to task. Rather the package will make use of the
  2966.  interrupts occuring sixty times a second to determine when to switch tasks..
  2967.  The Commodore 128 greatly simplifies things as in addation to the interrupts
  2968.  it also has the provision to relocate zero page and the stack page. So the
  2969.  package basically works by intercepting the IRQ vector, taking a look at the
  2970.  current job, saving the stack pointer, finding the next active job, loading
  2971.  the stack page and registers and resuming the normal IRQ as if nothing had
  2972.  ever happened.
  2973.  
  2974.  Unfortunatly Commodore never thought of having multiple programs in memory
  2975.  executing at any given time. Hence, problems will occur with file accesses,
  2976.  with memory contention, and with an over-all slowdown in speed. The package
  2977.  will detail how to handle device contentions, but it's recommended that
  2978.  programmers make use of the C= 128 kernal call LKUPLA $ff59 containing the
  2979.  logical file number they wish to use in .A; if the carry flag is set upon
  2980.  return then it is safe to use, else find another one as another program is
  2981.  using it. However, note that if you have multiple programs doing this then
  2982.  you may have problems with one grabbing a logical file number after the
  2983.  other process has checked for it. Multi-tasking is fun 'eh?  Problems like
  2984.  this will be examined when we get into semaphores later in this article..
  2985.  
  2986.  Craig Bruce's Dynamic Memory Allocation article in the second issue of C=
  2987.  Hacking should provide a very strong basis for a full-blown memoryy manager.
  2988.  With minor modifications (basically just changing the initial allocations so
  2989.  that the package is not killed) it should be able to work.  Also it will need
  2990.  changes to make sure that processes don't try to allocate at the same time.
  2991.  So a memory manager is not too much of a problem. Details of what changes
  2992.  will be necessary shall be in the next issue.
  2993.  
  2994.  What is a process? What is a program? I've been using the terms almost
  2995.  inter-changebly throughout this article at this point. Basically I'm calling
  2996.  them the same. A process, or program is defined as a program with it's own
  2997.  executable section, it's own data sections, and it's own stack and zero page.
  2998.  (Note, however, that the multi-tasking package does not support relocation of
  2999.  the zero page although this is likely to change).  The "kernal" of the
  3000.  multi-tasker is basically that part of the package which governs which
  3001.  process is executed or switched to next. Semaphores will be examined in
  3002.  detail later; they function as flags for processes too know when it is safe
  3003.  to execute something, and serve as signals betweenn them.
  3004.  
  3005.  Future versions of the package, (even though I know it does not exist out
  3006.  side of my house yet), will support pipes and a more strongly typed kernal
  3007.  so that processes may be prioritized.
  3008.  
  3009. II.   A Look At Multi-Tasking
  3010.  
  3011.  The introduction introduced some basic elements of multi-tasking but I'll
  3012.  repeat them here, defining them so that this article can be clear as some of
  3013.  the concepts can get a bit confusing.
  3014.  
  3015.     Background - A process is said to be in the "backgr ound" if it is not
  3016.     the foreground task and may or may not have input devi ces associated
  3017.     with it.
  3018.  
  3019.     Foreground - A process is said to be "foreground" if it is the main
  3020.     active process and is holding the keyboard and screen display captive
  3021.     (ie: the user is actually working within it).
  3022.   
  3023.     Kernal  - A small section of code that performs low-leval work that is 
  3024.     needed by any programs in memory..
  3025.  
  3026.     Multi-Tasking - Execution of more than one process at any given
  3027.     time.
  3028.  
  3029.     Priority - A value associated with each process that determines how
  3030.     often, and possibly when a process is executed.
  3031.  
  3032.     Process - The space in memory taken up by executable program code, any
  3033.     associated data, the stack and the registers associated and currently in
  3034.     use by it, including the current PC (program counter)..
  3035.  
  3036.     Semaphores - Values that are globally accessed by processes to share and
  3037.     communicate information between each other and the kernal.
  3038.  
  3039.  Some CPU's have available a multi- tasking mode (the 386 and 486 are the
  3040.  most famaliar ones that come to mind), y et the 8502 chip contained inside
  3041.  the Commodore 128 was first designed before 1985 and lacks multi-tasking. It
  3042.  would be nice if such a multi-tasking CPU in the 6502 family did exist but
  3043.  it would also create problems with the 6502 style architecture and wouldd
  3044.  produce severe compatibility problems.
  3045.  
  3046.  So how is the C=128 supposed to do multi-tasking? Well, we'll "simulate"
  3047.  it..
  3048.  
  3049.  Basically if we had two programs in machine language:
  3050.  
  3051.             Program 1:                            Program 2:
  3052.           - lda #65    ; the "A" character     - lda #64    ; the "@" character
  3053.             jsr $ffd2  ; print it                jsr $ffd2  ; print it
  3054.             jmp -                                jmp --
  3055.  
  3056.  And we wanted them to multi-task we'd expect something like the following:
  3057.  
  3058. @A@A@A@A@A@A@A@A@A@A@A@A@A@A@A@A@A@A@A@A@A@A@A@A@
  3059. A@A@A@A@A@A@A@A@A@A@A@A@A@AA
  3060.  
  3061.  It's unlikely that you'll get that in many multi-tasking environments,
  3062.  even non-simulated ones. Since we're only going to be switching tasks every
  3063.  1/60 of a second then we're more likely to see an output similair to this:
  3064.  
  3065. @@@@@@AAAAAAA@@@@@@@AAAAAAA@@@@@@@@AAAAAAA@@@@@@@
  3066. AAAAAAAA@@@@@@@@AAAAAA@@@@@@@
  3067.  
  3068.  So that it seems a process will run for about 1/60 of a second beforee
  3069.  switching to the next one.
  3070.   
  3071.  We run into problems however. The KERNAL in the C128 that contains most
  3072.  off the file handling, screen manipulations, and keyboard input routines. It
  3073.  was never designed with the idea of multi-tasking in mind. So we're gonna
  3074.  have code running in the KERNAL in two spots for the two differant processes
  3075.  and it's more than likely we'll end up with something like:
  3076.  
  3077. @@@@@@@@<and then a BRK or some strange error or never-never-land>>
  3078.  
  3079.   There's got to be some way to fix it - There is - It's called a semaphore..
  3080.  
  3081.  A semaphore is a value that is checked before access is granted to another
  3082.  group of memory locations. The semaphore is basically requested via the
  3083.  following:
  3084.  
  3085.         request_semaphore       sei
  3086.                                 ldx semaphore
  3087.                                 dex
  3088.                                 beq +
  3089.                                 cli
  3090.                               - ldy #$ff
  3091.                                 dey 
  3092.                                 bne -
  3093.                               + inc semaphore
  3094.                                 cli
  3095.  
  3096.  Now the request_semaphore has to disable interrupts to prevent another task
  3097.  from changing the semaphore value before this routine has had a chance. The
  3098.  actual code for the request_semaphore will be very similair to the above.
  3099.  
  3100.  Using a similair routine that performs the opposite - setting the semaphore
  3101.  to a zero value when finished we can dictate what program has control over
  3102.  what device or what memory areas.
  3103.  
  3104.  The semaphores will be used to govern access to the KERNAL routines which
  3105.  manipulate the locations in zero page etc, they'll also be used to manage the
  3106.  memory manager when it is implemented as it'd be awkward for it to allocate
  3107.  the same block of memory to two or more processes.
  3108.  
  3109. III.  Multi-Tasking Function Calls (Package Calls)
  3110.  
  3111.   OffSet | Name          | Notes
  3112.   -------+---------------+--------------------------------------------------
  3113.    $00   | SetUp         | .C=0 Init Package, .C=1 Uninstall Package
  3114.          |               |      (including Kernal re-direction).
  3115.    $03   | SpawnProcess  | On return: .C=0 parent, .C = 1 child
  3116.    $06   | ChangePriority| .A = new foreground priority, .X = new background
  3117.    $09   | KillThisProc  | Kills Calling Process (no return)
  3118.    $0c   | KillOtherProc | Kills Process # .A
  3119.    $0f   | RequestSemaph | Requests Semaphore #.X
  3120.    $12   | ReleaseSemaph | Releases Semaphore #.X
  3121.    $14   | GetProcInfo   | Returns Process Information, Input=#A
  3122.          |               |   of 0: Process Id
  3123.          |               |   of 1: Process Foreground Priority
  3124.          |               |   of 2: Process Background Priority
  3125.          |               |   of 3+ Other Information To Be Decided Later
  3126.    $17   | PipeInit      | .AY - Address of Pipe, .X = Size/8
  3127.          |               |    Return: .X - Pipe #
  3128.    $1a   | WritePipe     | .AY - Address of Null Term. Data, .X = Pipe #
  3129.    $1d   | ReadPipe      | .AY - Address to Put Data .X=Pipe #
  3130.    $20   | ....          |  \
  3131.    $2e   | ....          |   \ More Routines for the future.
  3132.   -------+---------------+--------------------------------------------------
  3133.  
  3134. IV.   Availibility of the Packagee
  3135.  
  3136. The package should be available at the time of the next issue. A further
  3137. examination of how the routines work shall be examined along with the source
  3138. code.
  3139.  
  3140. Errors popped up in developing it and rather than delay C= Hacking any
  3141. further I decided to go ahead and release the above information so that
  3142. individuals can start developing appropriate routines. In addition,
  3143. please note that PIPEs _may_ or may not be supported in the next issue.
  3144. I have not fully made up my mind yet on them.
  3145.  
  3146. V.    Referencess
  3147.  
  3148.   Born to Code in C, Herbert Schildt, Osborne-McGraw Hill, p.203-252.
  3149.  
  3150.   Notes from Operating Systems Course, Pembroke State Univ, Fall '92.
  3151.  
  3152. =============================================================================
  3153. LITTLE RED READER: MS-DOS file reader/WRITER for the C128 and 1571/81.
  3154. by Craig Bruce  (csbruce@neumann.uwaterloo.ca)
  3155.  
  3156. 1. INTRODUCTION
  3157.  
  3158. This article is a continuation of the Little Red Reader article from last
  3159. issue.  The program has been extended to write MS-DOS files, in addition to
  3160. reading them.  The program still works drive-to-drive so you'll still need two
  3161. disk drives (either physical or logical) to use it.  The program has also been
  3162. extended to allow MS-DOS files to be deleted and to allow the copying of
  3163. Commodore-DOS files between CBM-DOS disks (this makes it more convenient to
  3164. use the program with a temporary logical drive like RAMDOS).  Also, since I
  3165. have recently acquired a CMD FD-4000 floppy disk drive, I know that this
  3166. program works with MS-DOS disks with this drive (but only for the 720K
  3167. format).
  3168.  
  3169. The program still has the same organization as last time: a menu-oriented
  3170. user-interface program written in BASIC that makes use of a package of MS-DOS
  3171. disk accessing routines written in machine language.  Oh, this program is
  3172. Public Domain Software, so feel free to distribute and/or mangle it as you
  3173. wish.  Just note any manglings on the "initializing" screen so people don't
  3174. blame me.
  3175.  
  3176. The program runs on either the 40 or 80-column screens, but you will get
  3177. much better performance from the BASIC portion of the program by being
  3178. in 80-column mode and FAST mode.  A modification that someone might want
  3179. to make would be to spread-out the display for the 80-column screen and add
  3180. color to the rather bland display.
  3181.  
  3182. 2. USER GUIDE
  3183.  
  3184. LOAD and RUN the "lrr.128" BASIC program file.  When the program is first run,
  3185. it will display an "initializing" message and will load in the binary machine
  3186. language package from the "current" Commodore DOS drive (the current drive is
  3187. obtained from PEEK(186) - the last device accessed).  The binary package is
  3188. loaded only on the first run and is not reloaded on subsequent runs if the
  3189. package ID field is in place.
  3190.  
  3191. The system is designed to have two file selection menus: one for the MS-DOS
  3192. disk drive, and one for the Commodore-DOS disk drive (which may be a logical
  3193. disk drive).  The idea for copying is that you select the files in one of
  3194. these menus, and then program knows to copy them to the disk for the other
  3195. menu.  This idea of having two selection menus is also very consistent with
  3196. the original program.
  3197.  
  3198. 2.1. MS-DOS MENU
  3199.  
  3200. When the program starts, the MS-DOS menu of the program is displayed.  It
  3201. looks like:
  3202.  
  3203.    MS-DOS  MS=10:1581  CBM=8  FREE=715000
  3204.  
  3205.    NUM  S  TRN  TYP  FILENAME  EXT  LENGTH
  3206.    ---  -  ---  ---  --------  ---  ------
  3207.      1  *  ASC  SEQ  HACK4     TXT  120732
  3208.      2     BIN  PRG  RAMDOS    SFX   34923
  3209.  
  3210.    D=DIR M=MSDEV F=CBMDEV C=COPY Q=QUIT
  3211.    T=TOGGLE R=REMOVE X=CBMCPY /=MENU +-=PG
  3212.  
  3213. except that immediately after starting up, "<directory not loaded>" will be
  3214. displayed rather than filenames.  The menu looks and operates pretty much as
  3215. it did in the last issue of C= Hacking.  The only differences are that the
  3216. number of bytes free on the drive are displayed (which is useful to know when
  3217. writing files) and there are some more commands.
  3218.  
  3219. The directory ("D"), change ms-dos device ("M"), change commodore file device
  3220. ("F"), toggle column contents ("T"), copy ms-dos files to cbm-dos disk ("C"),
  3221. quit ("Q"), paging ("+" and "-"), column change (SPACE or RETURN), and the
  3222. cursor movement commands all work the same as before.  They are all sticks to
  3223. use to flog the beast into submission.  The new commands are: "R" (remove ==
  3224. delete), "/" (change menu), and "X" (copy CBM files == "Xerox").
  3225.  
  3226. The remove command is used to delete selected files from the MS-DOS disk.
  3227. After selecting this option, you will get an annoying "are you sure" question
  3228. and the the selected files will quickly disappear and the changes will finally
  3229. be written to disk.  Deleting a batch of MS-DOS files is much quicker than
  3230. deleting Commodore-DOS files since MS-DOS disks use a File Allocation Table
  3231. rather than the linked list of blocks organization that CBM uses.  In order to
  3232. make the BASIC program execute quicker, after deleting, the original order of
  3233. the filenames in the directory listing will be changed.  Be forewarned that
  3234. the delete operation is non-recoverable.
  3235.  
  3236. The change menu command is used to move back and forth between the Commodore-
  3237. DOS and MS-DOS menus.
  3238.  
  3239. 2.2. COMMODORE-DOS MENU
  3240.  
  3241. The Commodore-DOS menu, which displays the names of the Commodore files
  3242. selected for various operations, looks and works pretty much the same as
  3243. the MS-DOS menu:
  3244.  
  3245.    CBMDOS  MS=10:1581  CBM=8  FREE=3211476
  3246.  
  3247.    NUM  S  TRN  FILENAME         T  LENGTH
  3248.    ---  -  ---  ---------------- -  ------
  3249.      1  *  BIN  LRR-128          P    9876
  3250.      2     ASC  COM-HACKING-005  S  175412
  3251.  
  3252.    D=DIR M=MSDEV F=CBMDEV C=COPY Q=QUIT
  3253.    T=TOGGLE R=REMOVE X=CBMCPY /=MENU +-=PG
  3254.  
  3255. You'll notice, however, that the filetype field ("T" here) is moved and is
  3256. unchangable.  Also, the file lengths are not exact; they are reported as the
  3257. block count of the file multiplied by 254.  This menu is not maintained for
  3258. files being copied to the CBM-DOS disk from an MS-DOS disk.  You'll
  3259. have to re-execute the Directory instruction to get an updated listing.
  3260.  
  3261. The "D" (directory) command has local effect when in this menu.  The
  3262. Commodore-DOS directory will be loaded from the current CBM device number.
  3263. Note that in order for this to work, the CBM device must be number eight
  3264. or greater (a disk drive).  Originally, the subroutine for this command was
  3265. written using only GET#'s from the disk and was very slow.  It was modified,
  3266. however, to call a machine language subroutine to read the information for
  3267. a directory entry from the directory listing, and hence the subroutine now
  3268. operates at a tolerable speed.
  3269.  
  3270. The "C" (copy) command also has a different meaning when in this menu.  It
  3271. means to copy the selected CBM files to the MS-DOS disk.  See details below.
  3272.  
  3273. The copy CBM files ("X") command is used to copy the files in the CBM-DOS menu
  3274. to another CBM-DOS disk unit.  Select the files you want to copy and then
  3275. press X.  You will then be asked what device number you want to copy the files
  3276. to.  The device can be another disk drive or any other device (except the
  3277. keyboard).  Using device number 0 does not mean the "null" device as it does
  3278. with copying MS-DOS to CBM.  If you are copying to a disk device and the file
  3279. already exists, then you will be asked if you wish to overwrite the file.  You
  3280. cannot copy to the same disk unit.  Also, all files are copied in binary mode
  3281. (regardless of what translation you have selected for a file).
  3282.  
  3283. The copy CBM files command was included since all of the low-level gear
  3284. needed to implement it (specifically "commieIn" and "commieOut" below) was
  3285. also required by other functions.  This command can be very convenient when
  3286. working with RAMDOS.  For example, if you only had a 1571 as device 8 but you
  3287. have a RAM expander and have installed RAMDOS as device 9, then you would
  3288. copy MS-DOS files to RAMDOS using the MS-DOS menu, and then you would go to
  3289. the Commodore-DOS menu ("/"), read the directory, select all files, insert an
  3290. Commodore-DOS diskette into your 1571, and then use "X" to copy from the
  3291. RAMDOS device to the 1571.
  3292.  
  3293. The remove command ("R") does not work for this directory.  You can SCRATCH
  3294. your CBM-DOS files your damn self.
  3295.  
  3296. 2.3. COPY CBM-DOS TO MS-DOS
  3297.  
  3298. Before you can copy selected CBM-DOS files to an MS-DOS disk, the MS-DOS disk
  3299. directory must be already loaded (from the MS-DOS menu).  This is required
  3300. since the directory and FAT information are kept in memory at all times during
  3301. the execution of this program.
  3302.  
  3303. When you enter copy mode, the screen will clear and the name of each selected
  3304. file is displayed as it is being copied.  If an error is encountered on either
  3305. the MS-DOS or CBM-DOS drive during copying, an error message will be displayed
  3306. and copying will continue (after you press a key for MS-DOS errors).  Please
  3307. note that not a whole lot of effort was put into error recovery.
  3308.  
  3309. To generate an MS-DOS filename from an CBM-DOS filename, the following
  3310. algorithm is used.  The filename is searched from right to left for the last
  3311. "." character.  If there is no "." character, then the entire filename, up to
  3312. 11 characters, is used as the MS-DOS filename.  Characters 9 to 11 will be
  3313. used as the extension.  If there is a "." character, the all characters before
  3314. it, up to eight, will be used as the MS-DOS filename and all characters after
  3315. the final ".", up to three, will be used as the MS-DOS extension.
  3316.  
  3317. Then, the newly generated MS-DOS filename is scanned for any extra "."
  3318. characters or embedded spaces.  If any are found, they are replaced by the
  3319. underscore character ("_", which is the backarrow character on a Commodore
  3320. display).  Finally, all trailing underscores are removed from the end of both
  3321. the filename and extension portions of the MS-DOS filename.  Also, all
  3322. characters are converted to lowercase PETSCII (which is uppercase ASCII) when
  3323. they are copied into the MS-DOS filename.  Note that if the Commodore filename
  3324. is not in the 8/3 format of MS-DOS, then something in the name may be lost.
  3325. Some examples of filename conversion follow:
  3326.  
  3327. CBM-DOS FILENAME       MS-DOS FILENAME
  3328. ----------------       ---------------
  3329. "lrr.bin"              "lrr.bin"
  3330. "lrr.128.bin"          "lrr_128.bin"
  3331. "hello there.text"     "hello_th.tex"
  3332. "long_filename"        "long_fil.ena"
  3333. "file 1..3.s__5"       "file_1.s"
  3334.  
  3335. It would have been time-consuming to have the program scan the MS-DOS
  3336. directory for a filename already existing on the disk, so LRR will put
  3337. multiple files on a disk with the same filename without complaining.  This
  3338. also gets rid of the problem of asking you if you want to overwrite the old
  3339. file or generate a new name.  However, in order to retrieve the file from
  3340. disk on an MS-DOS machine, you will probably have to use the RENAME command to
  3341. rename the first versions of the file on the disk to something else so MS-DOS
  3342. will scan further in the directory for the last version of the file with the
  3343. same filename.  There is no rename command in LRR because I never thought of
  3344. it in time.  It would have been fairly easy to put in.
  3345.  
  3346. The date generated for a new MS-DOS file will be all zeros.  Some systems
  3347. interpret this as 12:00 am, 01-Jan-80 and others don't display a date at all
  3348. for this value.
  3349.  
  3350. The physical copying of the file is done completely in machine language and
  3351. nothing is displayed on the screen while this is happening, but you can follow
  3352. things by looking at the blinking lights and listening for clicks and grinds.
  3353.  
  3354. Since the FAT and directory are maintained in RAM during the entire copying
  3355. process and are only flushed to disk after the entire batch of files are
  3356. copied, copying is made more efficient, since there will be no costly seek
  3357. back to track 0 after writing each file (like MS-DOS does).  If you have a
  3358. number of small files to copy, then they will be knocked off in quick
  3359. succession, faster than many MS-DOS machines will copy them.
  3360.  
  3361. To simplify the implementation, the current track of disk blocks for writing
  3362. is not maintained like it is for reading.  Also, a writing interleave of 1:1
  3363. is used for a 1571, which is not optimal.  However, since writing is such a
  3364. slow operation anyway, and since the 1571 is particularly bad by insisting on
  3365. verifying blocks, not much more overhead is introduced than is already
  3366. present.
  3367.  
  3368. An interesting note about writing MS-DOS disks is that you can terminate LRR
  3369. in the middle of a copy (with STOP+RESTORE) or in the middle of copying a
  3370. batch of files, and the MS-DOS disk will remain in a perfectly consistent
  3371. state afterwards.  The state will be as if none of the files were copied.  The
  3372. reason is that the control information (the FAT and directory) is maintained
  3373. internally and is flushed only after copying is all completed.  But don't
  3374. terminate LRR while it is flushing the control information.
  3375.  
  3376. Here is a table of copying speeds for copying to 1571, 1581, and CMD FD-4000
  3377. disk units with ASC and BIN translation modes.  All figures are in bytes/
  3378. second, which includes both reading the byte from a C= disk and writing it to
  3379. the MS-DOS disk.  The average speed for either the read or write operation
  3380. individually will be twice the speed given below.  These results were obtained
  3381. from copying a 156,273 byte text file (the text of C= Hacking Issue #4).
  3382.  
  3383.    FROM   \ TO: FD-bin     FD-asc     81-bin     81-asc     71-bin     71-asc
  3384.    --------+    ------     ------     ------     ------     ------     ------
  3385.    RAMLink |     2,332      2,200      2,332      2,200      1,594      1,559
  3386.    RAMDOS  |     1,070      1,053      1,604      1,600      1,561      1,510
  3387.    FD4000  |         -          -      1,645      1,597      1,499      1,464
  3388.    JD1581  |     1,662      1,619          -          -      1,474      1,440
  3389.    JD1571  |     1,050      1,024        953        933          -          -
  3390.  
  3391. These figures are for transfer speed only, not counting the couple of seconds
  3392. of opening files and flushing the directory.  Note that all my physical drives
  3393. are JiffyDOS-ified, so your performance may be slower.  I am at a loss to
  3394. explain why an FD-4000 is so much slower than a 1581 for copying from a
  3395. RAMDOS file, but the same speed or better for copying from anything else.
  3396.  
  3397. Since I don't have access to an actual MS-DOS machine, I have not tested the
  3398. files written onto an MS-DOS disk by LRR, except by reading them back with LRR
  3399. and BBR.  I do know, however, that earlier encarnations of this program did
  3400. work fine with MS-DOS machines.
  3401.  
  3402. 3. MS-DOS ROOT DIRECTORY
  3403.  
  3404. It was brought to my attention that I made a mistake in the pervious article.
  3405. I was wrong about the offset of the attributes field in a directory entry.
  3406. The layout should have been as follows:
  3407.  
  3408.    OFFSET     LEN     DESCRIPTION
  3409.    ------     ---     -----------
  3410.      0..7       8     Filename
  3411.     8..10       3     Extension
  3412.        11       1     Attributes: $10=Directory, $08=VolumeId
  3413.    12..21      10     <unused>
  3414.    22..25       4     Date
  3415.    26..27       2     Starting FAT entry number
  3416.    28..31       4     File length in bytes
  3417.  
  3418. 4. FILE COPYING PACKAGE
  3419.  
  3420. As was mentioned above, Little Red Reader is split into two pieces: a BASIC
  3421. front-end user interface program and a package of machine language subroutines
  3422. for disk accessing.  The BASIC program handles the menu, user interaction, and
  3423. most of the MS-DOS directory searching/modifying.  The machine language
  3424. package handles the hardware input/output, File Allocation Table and file
  3425. structure manipulations.
  3426.  
  3427. The file copying package is written in assembly language and is loaded into
  3428. memory at address $8000 on bank 0 and requires about 13K of memory.  The
  3429. package is loaded at this high address to be out of the way of the main BASIC
  3430. program, even if RAMDOS is installed.
  3431.  
  3432. This section of the article is presented in its entirety, including all of the
  3433. information given last time.
  3434.  
  3435. 4.1. INTERFACE
  3436.  
  3437. The subroutine call interface to the file copying package is summarized as
  3438. follows:
  3439.  
  3440.    ADDRESS     DESCRIPTION
  3441.    -------     -----------
  3442.    PK          initPackage subroutine
  3443.    PK+3        msDir    (load MS-DOS directory/FAT) subroutine
  3444.    PK+6        msRead   (copy MS-DOS to CBM-DOS) subroutine
  3445.    PK+9        msWrite  (copy CBM-DOS to MS-DOS) subroutine
  3446.    PK+12       msFlush  subroutine
  3447.    PK+15       msDelete subroutine
  3448.    PK+18       msFormat subroutine [not implemented]
  3449.    PK+21       msBytesFree subroutine
  3450.    PK+24       cbmCopy  (copy CBM-DOS to CBM-DOS) subroutine
  3451.    PK+27       cbmDirent (read CBM-DOS directory entry) subroutine
  3452.  
  3453. where "PK" is the load address of the package ($8000).
  3454.  
  3455. The parameter passing interface is summarized as follows:
  3456.  
  3457.    ADDRESS     DESCRIPTION
  3458.    -------     -----------
  3459.    PV          two-byte package identification number ($CB, 132)
  3460.    PV+2        errno : error code returned
  3461.    PV+3        MS-DOS device number (8 to 30)
  3462.    PV+4        MS-DOS device type ($00=1571, $FF=1581)
  3463.    PV+5        two-byte starting cluster number for file copying
  3464.    PV+7        low and mid bytes of file length for copying
  3465.    PV+9        pointer to MS-DOS directory entry for writing
  3466.    PV+11       CBM-DOS file block count
  3467.    PV+13       CBM-DOS file type ("S"=seq, "P"=prg, etc.)
  3468.    PV+14       CBM-DOS filename length
  3469.    PV+15       CBM-DOS filename characters (max 16 chars)
  3470.  
  3471. Where "PV" is equal to PK+30.  Additional subroutine parameters are passed in
  3472. the processor registers.
  3473.  
  3474. The MS-DOS device number and device type interface variables allow you to set
  3475. the MS-DOS drive and the package identification number allows the application
  3476. program to check if the package is already loaded into memory so that it only
  3477. has to load the package the first time the application is run and not on
  3478. re-runs.  The identification sequence is a value of $CB followed by a value of
  3479. 132.
  3480.  
  3481. 4.1.1. INIT_PACKAGE SUBROUTINE
  3482.  
  3483. The "initPackage" subroutine should be called when the package is first
  3484. installed, whenever the MS-DOS device number is changed, and whenever a new
  3485. disk is mounted to invalidate the internal track cache.  It requires no
  3486. parameters.
  3487.  
  3488. 4.1.2. MS_DIR SUBROUTINE
  3489.  
  3490. The "msDir" subroutine will load the directory, FAT, and the Boot Sector
  3491. parameters into the internal memory of the package from the current MS-DOS
  3492. device number.  No (other) input parameters are needed and the subroutine
  3493. returns a pointer to the directory space in the .AY registers and the number
  3494. of directory entries in the .X register.  If an error occurs, then the
  3495. subroutine returns with the Carry flag set and the error code is available in
  3496. the "errno" interface variable.  The directory entry data is in the directory
  3497. space as it was read in raw from the directory sectors on the MS-DOS disk.
  3498.  
  3499. 4.1.3. MS_READ SUBROUTINE
  3500.  
  3501. The "msRead" subroutine will copy a single file from the MS-DOS disk to a
  3502. specified CBM-Kernal logical file number (the CBM file must already be
  3503. opened).  If the CBM logical file number is zero, then the file data is simply
  3504. discarded after it is read from the MS-DOS file.  The starting cluster number
  3505. of the file to copy and the low and mid bytes of the file length are passed in
  3506. the PV+5 and PV+7 interface words.  The translation mode to use is passed in
  3507. the .A register ($00=binary, $FF=ascii) and the CBM logical file number to
  3508. output to is passed in the .X register.  If an error occurs, the routine
  3509. returns with the Carry flag set and the error code in the "errno" interface
  3510. variable.  There are no other output parameters.
  3511.  
  3512. Note that since the starting cluster number and low-file length of the file to
  3513. be copied are required rather than the filename, it is the responsibility of
  3514. the front-end application program to dig through the raw directory sector data
  3515. to get this information.  The application must also open the Commodore-DOS
  3516. file of whatever filetype on whatever device is required; the package does not
  3517. need to know the Commodore-DOS device number.
  3518.  
  3519. 4.1.4. MS_WRITE SUBROUTINE
  3520.  
  3521. The "msWrite" subroutine copies a single file from a specified CBM-Kernal
  3522. logical file number to a MS-DOS file.  The MS-DOS device number and type are
  3523. set above.  A pointer to the MS-DOS directory entry in the buffer returned by
  3524. the "msDir" call must be given in interface word PV+9 and the translation mode
  3525. and CBM lfn are passed in the .A and .X registers as in the "msRead" routine.
  3526. An error return is given in the usual way (.CS, errno).  Otherwise, there are
  3527. no return values.
  3528.  
  3529. It is the responsibility of the calling program to initialize the MS-DOS
  3530. directory entry to all zeros and then set the filename and set the starting
  3531. cluster pointer to $0FFF.  This routine will update the starting cluster and
  3532. file length fields of the directory entry when it finishes.  The internal
  3533. "dirty flags" are modified so that the directory and FAT will be flushed on
  3534. the next call to "msFlush".
  3535.  
  3536. 4.1.5. MS_FLUSH SUBROUTINE
  3537.  
  3538. The "msFlush" subroutine takes no input parameters other than the implicit
  3539. msDevice and msType.  If "dirty" (modified), the FAT will be written to the
  3540. MS-DOS disk, to both physical replicas of the disk FAT.  Then, each directory
  3541. sector that is dirty will be written to disk.  After flushing, the internal
  3542. dirty flags will be cleared.  An error return is given in the usual way.
  3543. There are no other output parameters.  If you call this routine and there are
  3544. no dirty flags set, then it will return immediately, without any writing to
  3545. disk.
  3546.  
  3547. 4.1.6. MS_DELETE SUBROUTINE
  3548.  
  3549. The "msDelete" subroutine will deallocate all File Allocation Table entries
  3550. (and hence, data clusters) allocated to a file and mark the directory entry as
  3551. being deleted (by putting an $E5 into the first character of the filename).
  3552. The file is specified by giving the pointer to the directory entry in
  3553. interface word at PV+9.  After deallocating the file data, the internal
  3554. "dirty" flag will be set for the FAT and the sector that the directory entry
  3555. is on, but nothing will be written to disk.  There is no error return from
  3556. this routine.  It is the responsibility of the calling routine to eventually
  3557. call the "msFlush" routine.
  3558.  
  3559. 4.1.7. MS_FORMAT SUBROUTINE
  3560.  
  3561. The "msFormat" subroutine is not implemented.  It's intended function was to
  3562. format the MS-DOS disk and generate and write the boot sector, initial FAT,
  3563. and initial directory entry data.
  3564.  
  3565. 4.1.8. MS_BYTES_FREE SUBROUTINE
  3566.  
  3567. The "msBytesFree" subroutine will scan the currently loaded MS-DOS File
  3568. Allocation Table, count the number of clusters free, and return the number of
  3569. bytes free for file storage on the disk.  There are no input parameters and
  3570. the bytes free are returned in the .AYX registers (.A=low, .Y=mid, .X=high
  3571. byte).  The subroutine has no error returns and does not check if an MS-DOS
  3572. directory is actually loaded.
  3573.  
  3574. 4.1.9. CBM_COPY SUBROUTINE
  3575.  
  3576. The "cbmCopy" subroutine will copy from an input CBM-Kernal logical file
  3577. number given in the .A register to an output CBM-Kernal lfn given in the .X
  3578. register, in up to 1024 byte chunks.  File contents are copied exactly (no
  3579. translation).  This routine does not care if the lfn's are on the same device
  3580. or not, but the input device must be a disk unit (either logical or physical).
  3581. An error return is given in the usual way.
  3582.  
  3583. 4.1.10. CBM_DIRENT SUBROUTINE
  3584.  
  3585. The "cbmDirent" subroutine reads the next directory entry from the CBM-Kernal
  3586. lfn given in .A and puts the data into interface variables.  Of course, the
  3587. lfn is assumed to be open for reading a directory ("$").  The block count is
  3588. returned in the word at PV+11, the first character of the filetype is returned
  3589. at PV+13, the number of characters in the filename is returned in PV+14, and
  3590. the filename characters are returned in bytes PV+15 to PV+30.  An error return
  3591. is given in the usual way.
  3592.  
  3593. This routine assumes that the first two bytes of the directory file have
  3594. already been read.  The first call to this routine will return the name of the
  3595. disk.  The end of a directory is signalled by a filename length of zero.  In
  3596. this case, the block count returned will be the number of blocks free on the
  3597. disk.
  3598.  
  3599. 4.2. IMPLEMENTATION
  3600.  
  3601. This section presents the code that implements the MS-DOS file reading and
  3602. writing package.  It is here in a special form; each code line is preceded by
  3603. the % symbol.  The % sign is there to allow you to easily extract the
  3604. assembler code from the rest of this magazine (and all of my ugly comments).
  3605. On a Unix system, all you have to do is execute the following command line
  3606. (substitute filenames as appropriate):
  3607.  
  3608. grep '^%' Hack5 | sed 's/^% //' | sed 's/^%//' >lrr.s
  3609.  
  3610. % ; Little Red Reader/Writer utility package by Craig Bruce, 31-Jan-92
  3611. % ; Written for C= Hacking Net-Magazine; for C-128, 1571, 1581
  3612. %
  3613.  
  3614. The code is written for the Buddy assembler and here are a couple setup
  3615. directives.  Note that my comments come before the section of code.
  3616.  
  3617. % .org $8000
  3618. % .obj "lrr.bin"
  3619. %
  3620. % ;====jump table and parameters interface ====
  3621. %
  3622. % jmp initPackage ;()
  3623. % jmp msDir       ;( msDevice, msType ) : .AY=dirAddr, .X=direntCount
  3624. % jmp msRead      ;( msDevice, msType, startCluster, lenML,.A=trans,.X=cbmLfn )
  3625. % jmp msWrite     ;( msDevice, msType, writeDirent, .A=trans, .X=cbmLfn )
  3626. % jmp msFlush     ;( msDevice, msType )
  3627. % jmp msDelete    ;( writeDirent )
  3628. % jmp msFormat    ;( msDevice, msType )
  3629. % jmp msBytesFree ;( ) : .AYX=bytesFree
  3630. % jmp cbmCopy     ;( .A=inLfn, .X=outLfn )
  3631. % jmp cbmDirent   ;( .A=lfn )
  3632. %
  3633. % .byte $cb,132   ;identification (location pk+30)
  3634.  
  3635. These interface variables are included in the package program space to
  3636. minimize unwanted interaction with other programs loaded at the same time,
  3637. such as the RAMDOS device driver.
  3638.  
  3639. % errno           .buf 1
  3640. % msDevice        .buf 1
  3641. % msType          .buf 1    ;$00=1571, $ff=1581
  3642. % startCluster    .buf 2
  3643. % lenML           .buf 2    ;length medium and low bytes
  3644. % writeDirent     .buf 2    ;pointer to dirent
  3645. % cdirBlocks      .buf 2    ;cbm dirent blocks
  3646. % cdirType        .buf 1    ;cbm dirent filetype
  3647. % cdirFlen        .buf 1    ;cbm dirent filename length
  3648. % cdirName        .buf 16   ;cbm dirent filename
  3649. %
  3650.  
  3651. This command is not currently implemented.  Its stub appears here.
  3652.  
  3653. % msFormat = *
  3654. %    brk
  3655. %
  3656. % ;====global declaraions====
  3657. %
  3658. % kernelListen = $ffb1
  3659. % kernelSecond = $ff93
  3660. % kernelUnlsn  = $ffae
  3661. % kernelAcptr  = $ffa2
  3662. % kernelCiout  = $ffa8
  3663. % kernelSpinp  = $ff47
  3664. % kernelChkin  = $ffc6
  3665. % kernelChkout = $ffc9
  3666. % kernelClrchn = $ffcc
  3667. % kernelChrin  = $ffcf
  3668. % kernelChrout = $ffd2
  3669. %
  3670. % st = $90
  3671. % ciaClock = $dd00
  3672. % ciaFlags = $dc0d
  3673. % ciaData  = $dc0c
  3674. %
  3675.  
  3676. These are the parameters and derived parameters from the boot sector.  They
  3677. are kept in the program space to avoid interactions.
  3678.  
  3679. % clusterBlockCount .buf 1        ;1 or 2
  3680. % fatBlocks         .buf 1        ;up to 3
  3681. % rootDirBlocks     .buf 1        ;up to 8
  3682. % rootDirEntries    .buf 1        ;up to 128
  3683. % totalSectors      .buf 2        ;up to 1440
  3684. % firstFileBlock    .buf 1
  3685. % firstRootDirBlock .buf 1
  3686. % fileClusterCount  .buf 2
  3687. % lastFatEntry      .buf 2
  3688. %
  3689.  
  3690. The cylinder (track) and side that is currently stored in the track cache
  3691. for reading.
  3692.  
  3693. % bufCylinder     .buf 1
  3694. % bufSide         .buf 1
  3695.  
  3696. These "dirty" flags record what has to be written out for a flush operation.
  3697.  
  3698. % fatDirty        .buf 1
  3699. % dirDirty        .buf 8  ;flag for each directory block
  3700. % formatParms     .buf 6
  3701. %
  3702.  
  3703. This package is split into a number of levels.  This level interfaces with the
  3704. Kernal serial bus routines and the burst command protocol of the disk drives.
  3705.  
  3706. % ;====hardware level====
  3707. %
  3708.  
  3709. Connect to the MS-DOS device and send the "U0" burst command prefix and the
  3710. burst command byte.
  3711.  
  3712. % sendU0 = *  ;( .A=burstCommandCode ) : .CS=err
  3713. %    pha
  3714. %    lda #0
  3715. %    sta st
  3716. %    lda msDevice
  3717. %    jsr kernelListen
  3718. %    lda #$6f
  3719. %    jsr kernelSecond
  3720. %    lda #"u"
  3721. %    jsr kernelCiout
  3722. %    bit st
  3723. %    bmi sendU0Error
  3724. %    lda #"0"
  3725. %    jsr kernelCiout
  3726. %    pla
  3727. %    jsr kernelCiout
  3728. %    bit st
  3729. %    bmi sendU0Error
  3730. %    clc
  3731. %    rts
  3732. %
  3733. %    sendU0Error = *
  3734. %    lda #5
  3735. %    sta errno
  3736. %    sec
  3737. %    rts
  3738. %
  3739.  
  3740. Toggle the "Data Accepted / Ready For More" clock signal for the burst
  3741. transfer protocol.
  3742.  
  3743. % toggleClock = *
  3744. %    lda ciaClock
  3745. %    eor #$10
  3746. %    sta ciaClock
  3747. %    rts
  3748. %
  3749.  
  3750. Wait for a burst byte to arrive in the serial data register of CIA#1 from the
  3751. fast serial bus.
  3752.  
  3753. % serialWait = *
  3754. %    lda #$08
  3755. % -  bit ciaFlags
  3756. %    beq -
  3757. %    rts
  3758. %
  3759.  
  3760. Wait for and get a burst byte from the fast serial bus, and send the "Data
  3761. Accepted" signal.
  3762.  
  3763. % getBurstByte = *
  3764. %    jsr serialWait
  3765. %    ldx ciaData
  3766. %    jsr toggleClock
  3767. %    txa
  3768. %    rts
  3769. %
  3770.  
  3771. Send the burst commands to "log in" the MS-DOS disk and set the Read sector
  3772. interleave factor.
  3773.  
  3774. % mountDisk = *  ;() : .CS=err
  3775. %    lda #%00011010
  3776. %    jsr sendU0
  3777. %    bcc +
  3778. %    rts
  3779. % +  jsr kernelUnlsn
  3780. %    bit st
  3781. %    bmi sendU0Error
  3782. %    clc
  3783. %    jsr kernelSpinp
  3784. %    bit ciaFlags
  3785. %    jsr toggleClock
  3786. %    jsr getBurstByte
  3787. %    sta errno
  3788. %    and #$0f
  3789. %    cmp #2
  3790. %    bcs mountExit
  3791.  
  3792. Grab the throw-away parameters from the mount operation.
  3793.  
  3794. %    ldy #0
  3795. % -  jsr getBurstByte
  3796. %    sta formatParms,y
  3797. %    iny
  3798. %    cpy #6
  3799. %    bcc -
  3800. %    clc
  3801.  
  3802. Set the Read sector interleave to 1 for a 1581 or 4 for a 1571.
  3803.  
  3804. %    ;** set interleave
  3805. %    lda #%00001000
  3806. %    jsr sendU0
  3807. %    bcc +
  3808. %    rts
  3809. % +  lda #1            ;interleave of 1 for 1581
  3810. %    bit msType
  3811. %    bmi +
  3812. %    lda #4            ;interleave of 4 for 1571
  3813. % +  jsr kernelCiout
  3814. %    jsr kernelUnlsn
  3815. %    mountExit = *
  3816. %    rts
  3817. %
  3818.  
  3819. Read all of the sectors of a given track into the track cache.
  3820.  
  3821. % bufptr = 2
  3822. % secnum = 4
  3823. %
  3824. % readTrack = *  ;( .A=cylinder, .X=side ) : trackbuf, .CS=err
  3825. %    pha
  3826. %    txa
  3827.  
  3828. Get the side and put it into the command byte.  Remember that we have to flip
  3829. the side bit for a 1581.
  3830.  
  3831. %    and #$01
  3832. %    asl
  3833. %    asl
  3834. %    asl
  3835. %    asl
  3836. %    bit msType
  3837. %    bpl +
  3838. %    eor #$10
  3839. % +  jsr sendU0
  3840. %    pla
  3841. %    bcc +
  3842. %    rts
  3843. % +  jsr kernelCiout      ;cylinder number
  3844. %    lda #1               ;start sector number
  3845. %    jsr kernelCiout
  3846. %    lda #9               ;sector count
  3847. %    jsr kernelCiout
  3848. %    jsr kernelUnlsn
  3849.  
  3850. Prepare to receive the track data.
  3851.  
  3852. %    sei
  3853. %    clc
  3854. %    jsr kernelSpinp
  3855. %    bit ciaFlags
  3856. %    jsr toggleClock
  3857. %    lda #<trackbuf
  3858. %    ldy #>trackbuf
  3859. %    sta bufptr
  3860. %    sty bufptr+1
  3861.  
  3862. Get the sector data for each of the 9 sectors of the track.
  3863.  
  3864. %    lda #0
  3865. %    sta secnum
  3866. % -  bit msType
  3867. %    bmi +
  3868.  
  3869. If we are dealing with a 1571, we have to set the buffer pointer for the next
  3870. sector, taking into account the soft interleave of 4.
  3871.  
  3872. %    jsr get1571BufPtr
  3873. % +  jsr readSector
  3874. %    bcs trackExit
  3875. %    inc secnum
  3876. %    lda secnum
  3877. %    cmp #9
  3878. %    bcc -
  3879. %    clc
  3880. %    trackExit = *
  3881. %    cli
  3882. %    rts
  3883. %
  3884.  
  3885. Get the buffer pointer for the next 1571 sector.
  3886.  
  3887. % get1571BufPtr = *
  3888. %    lda #<trackbuf
  3889. %    sta bufptr
  3890. %    ldx secnum
  3891. %    clc
  3892. %    lda #>trackbuf
  3893. %    adc bufptr1571,x
  3894. %    sta bufptr+1
  3895. %    rts
  3896. %
  3897. % bufptr1571 = *
  3898. %    .byte 0,8,16,6,14,4,12,2,10
  3899. %
  3900.  
  3901. Read an individual sector into memory at the specified address.
  3902.  
  3903. % readSector = *  ;( bufptr ) : .CS=err
  3904.  
  3905. Get and check the burst status byte for errors.
  3906.  
  3907. %    jsr getBurstByte
  3908. %    sta errno
  3909. %    and #$0f
  3910. %    cmp #2
  3911. %    bcc +
  3912. %    rts
  3913. % +  ldx #2
  3914. %    ldy #0
  3915. %
  3916.  
  3917. Receive the 512 sector data bytes into memory.
  3918.  
  3919. %    readByte = *
  3920. %    lda #$08
  3921. % -  bit ciaFlags
  3922. %    beq -
  3923. %    lda ciaClock
  3924. %    eor #$10
  3925. %    sta ciaClock
  3926. %    lda ciaData
  3927. %    sta (bufptr),y
  3928. %    iny
  3929. %    bne readByte
  3930. %    inc bufptr+1
  3931. %    dex
  3932. %    bne readByte
  3933. %    rts
  3934. %
  3935. % oldClock = 5
  3936. %
  3937.  
  3938. Write an individual sector to disk, from a specified memory address.
  3939.  
  3940. % writeSector = *  ;( bufptr, .A=track, .X=side, .Y=sector ) : .CS=err
  3941. %    pha
  3942. %    sty secnum
  3943.  
  3944. Get the side into the burst command byte
  3945.  
  3946. %    txa
  3947. %    and #$01
  3948. %    asl
  3949. %    asl
  3950. %    asl
  3951. %    asl
  3952. %    ora #$02
  3953. %    bit msType
  3954. %    bpl +
  3955. %    eor #$10
  3956. % +  jsr sendU0
  3957. %    pla
  3958. %    bcc +
  3959. %    rts
  3960.  
  3961. Send rest of parameters for burst command.
  3962.  
  3963. % +  jsr kernelCiout      ;track number
  3964. %    lda secnum           ;sector number
  3965. %    jsr kernelCiout
  3966. %    lda #1               ;sector count
  3967. %    jsr kernelCiout
  3968. %    jsr kernelUnlsn
  3969. %    sei
  3970. %    lda #$40
  3971. %    sta oldClock
  3972. %    sec
  3973. %    jsr kernelSpinp      ;set for burst output
  3974. %    sei
  3975. %    bit ciaFlags
  3976. %    ldx #2
  3977. %    ldy #0
  3978. %
  3979.  
  3980. Write the 512 bytes for the sector.
  3981.  
  3982. %    writeByte = *
  3983. %    lda ciaClock
  3984. %    cmp ciaClock
  3985. %    bne writeByte
  3986. %    eor oldClock
  3987. %    and #$40
  3988. %    beq writeByte
  3989. %    lda (bufptr),y
  3990. %    sta ciaData
  3991. %    lda oldClock
  3992. %    eor #$40
  3993. %    sta oldClock
  3994. %    lda #8
  3995. % -  bit ciaFlags
  3996. %    beq -
  3997. %    iny
  3998. %    bne writeByte
  3999. %    inc bufptr+1
  4000. %    dex
  4001. %    bne writeByte
  4002. %
  4003.  
  4004. Read back the burst status byte to see if anything went wrong with the write.
  4005.  
  4006. %    clc
  4007. %    jsr kernelSpinp
  4008. %    bit ciaFlags
  4009. %    jsr toggleClock
  4010. %    jsr serialWait
  4011. %    ldx ciaData
  4012. %    jsr toggleClock
  4013. %    txa
  4014. %    sta errno
  4015. %    and #$0f
  4016. %    cmp #2
  4017. %    cli
  4018. %    rts
  4019. %
  4020.  
  4021. This next level of routines deals with logical sectors and the track cache
  4022. rather than with hardware.
  4023.  
  4024. % ;====logical sector level====
  4025. %
  4026.  
  4027. Invalidate the track cache if the MS-DOS drive number is changed or if a new
  4028. disk is inserted.  This routine has to establish a RAM configuration of $0E
  4029. since it will be called from RAM0.  Configuration $0E gives RAM0 from $0000 to
  4030. $BFFF, Kernal ROM from $C000 to $FFFF, and the I/O space over the Kernal from
  4031. $D000 to $DFFF.  This configuration is set by all application interface
  4032. subroutines.
  4033.  
  4034. % initPackage = *
  4035. %    lda #$0e
  4036. %    sta $ff00
  4037. %    lda #$ff
  4038. %    sta bufCylinder
  4039. %    sta bufSide
  4040. %    ldx #7
  4041. % -  sta dirDirty,x
  4042. %    dex
  4043. %    bpl -
  4044. %    sta fatDirty
  4045. %    clc
  4046. %    rts
  4047. %
  4048.  
  4049. Locate a sector (block) in the track cache, or read the corresponding physical
  4050. track into the track cache if necessary.  This routine accepts the cylinder,
  4051. side, and sector numbers of the block.
  4052.  
  4053. % sectorSave = 5
  4054. %
  4055. % readBlock = *  ;( .A=cylinder,.X=side,.Y=sector ) : .AY=blkPtr,.CS=err
  4056.  
  4057. Check if the correct track is in the track cache.
  4058.  
  4059. %    cmp bufCylinder
  4060. %    bne readBlockPhysical
  4061. %    cpx bufSide
  4062. %    bne readBlockPhysical
  4063.  
  4064. If so, then locate the sector's address and return that.
  4065.  
  4066. %    dey
  4067. %    tya
  4068. %    asl
  4069. %    clc
  4070. %    adc #>trackbuf
  4071. %    tay
  4072. %    lda #<trackbuf
  4073. %    clc
  4074. %    rts
  4075. %
  4076.  
  4077. Here, we have to read the physical track into the track cache.  We save the
  4078. input parameters and call the hardware-level track-reading routine.
  4079.  
  4080. %    readBlockPhysical = *
  4081. %    sta bufCylinder
  4082. %    stx bufSide
  4083. %    sty sectorSave
  4084. %    jsr readTrack
  4085.  
  4086. Check for errors.
  4087.  
  4088. %    bcc readBlockPhysicalOk
  4089. %    lda errno
  4090. %    and #$0f
  4091. %    cmp #11    ;disk change
  4092. %    beq +
  4093. %    sec
  4094. %    rts
  4095.  
  4096. If the error that happened is a "Disk Change" error, then mount the disk and
  4097. try to read the physical track again.
  4098.  
  4099. % +  jsr mountDisk
  4100. %    lda bufCylinder
  4101. %    ldx bufSide
  4102. %    ldy sectorSave
  4103. %    bcc readBlockPhysical
  4104. %    rts
  4105. %
  4106.  
  4107. Here, the physical track has been read into the track cache ok, so we recover
  4108. the original input parameters and try the top of the routine again.
  4109.  
  4110. %    readBlockPhysicalOk = *
  4111. %    lda bufCylinder
  4112. %    ldx bufSide
  4113. %    ldy sectorSave
  4114. %    jmp readBlock
  4115. %
  4116.  
  4117. Divide the given number by 18.  This is needed for the calculations to convert
  4118. a logical sector number to the corresponding physical cylinder, side, and
  4119. sector numbers that the lower-level routines require.  The method of repeated
  4120. subtraction is used.  This routine would probably work faster if we tried to
  4121. repeatedly subtract 360 (18*20) at the top, but I didn't bother.
  4122.  
  4123. % divideBy18 = *  ;( .AY=number ) : .A=quotient, .Y=remainder
  4124. %    ;** could repeatedly subtract 360 here
  4125. %    ldx #$ff
  4126. % -  inx
  4127. %    sec
  4128. %    sbc #18
  4129. %    bcs -
  4130. %    dey
  4131. %    bpl -
  4132. %    clc
  4133. %    adc #18
  4134. %    iny
  4135. %    tay
  4136. %    txa
  4137. %    rts
  4138. %
  4139.  
  4140. Convert the given logical block number to the corresponding physical cylinder,
  4141. side, and sector numbers.  This routine follows the formulae given in the
  4142. previous article with a few simplifying tricks.
  4143.  
  4144. % convertLogicalBlockNum = *  ;( .AY=blockNum ) : .A=cyl, .X=side,.Y=sec
  4145. %    jsr divideBy18
  4146. %    ldx #0
  4147. %    cpy #9
  4148. %    bcc +
  4149. %    pha
  4150. %    tya
  4151. %    sbc #9
  4152. %    tay
  4153. %    pla
  4154. %    ldx #1
  4155. % +  iny
  4156. %    rts
  4157. %
  4158.  
  4159. Copy a sequential group of logical sectors into memory.  This routine is used
  4160. by the directory loading routine to load the FAT and Root Directory, and is
  4161. used by the cluster reading routine to retrieve all of the blocks of a
  4162. cluster.  After the given starting logical sector number is converted into its
  4163. physical cylinder, side, and sector equivalent, the physical values are
  4164. incremented to get the address of successive sectors of the group.  This
  4165. avoids the overhead of the logical to physical conversion.  Quite a number of
  4166. temporaries are needed.
  4167.  
  4168. % destPtr = 6
  4169. % curCylinder = 8
  4170. % curSide = 9
  4171. % curSector = 10
  4172. % blockCountdown = 11
  4173. % sourcePtr = 12
  4174. %
  4175. % copyBlocks = *  ;( .AY=startBlock, .X=blockCount, ($6)=dest ) : .CS=err
  4176. %    stx blockCountdown
  4177. %    jsr convertLogicalBlockNum
  4178. %    sta curCylinder
  4179. %    stx curSide
  4180. %    sty curSector
  4181. %
  4182. %    copyBlockLoop = *
  4183. %    lda curCylinder
  4184. %    ldx curSide
  4185. %    ldy curSector
  4186. %    jsr readBlock
  4187. %    bcc +
  4188. %    rts
  4189. % +  sta sourcePtr
  4190. %    sty sourcePtr+1
  4191. %    ldx #2
  4192. %    ldy #0
  4193.  
  4194. Here I unroll the copying loop a little bit to cut the overhead of the branch
  4195. instruction in half.  (A cycle saved... you know).
  4196.  
  4197. % -  lda (sourcePtr),y
  4198. %    sta (destPtr),y
  4199. %    iny
  4200. %    lda (sourcePtr),y
  4201. %    sta (destPtr),y
  4202. %    iny
  4203. %    bne -
  4204. %    inc sourcePtr+1
  4205. %    inc destPtr+1
  4206. %    dex
  4207. %    bne -
  4208.  
  4209. Increment the cylinder, side, sector values.
  4210.  
  4211. %    inc curSector
  4212. %    lda curSector
  4213. %    cmp #10
  4214. %    bcc +
  4215. %    lda #1
  4216. %    sta curSector
  4217. %    inc curSide
  4218. %    lda curSide
  4219. %    cmp #2
  4220. %    bcc +
  4221. %    lda #0
  4222. %    sta curSide
  4223. %    inc curCylinder
  4224. % +  dec blockCountdown
  4225. %    bne copyBlockLoop
  4226. %    clc
  4227. %    rts
  4228. %
  4229.  
  4230. Convert a given cluster number into the first corresponding logical block
  4231. number.
  4232.  
  4233. % convertClusterNum = *  ;( .AY=clusterNum ) : .AY=logicalBlockNum
  4234. %    sec
  4235. %    sbc #2
  4236. %    bcs +
  4237. %    dey
  4238. % +  ldx clusterBlockCount
  4239. %    cpx #1
  4240. %    beq +
  4241. %    asl
  4242. %    sty 7
  4243. %    rol 7
  4244. %    ldy 7
  4245. % +  clc
  4246. %    adc firstFileBlock
  4247. %    bcc +
  4248. %    iny
  4249. % +  rts
  4250. %
  4251.  
  4252. Read a cluster into the Cluster Buffer, given the cluster number.  The cluster
  4253. number is converted to a logical sector number and then the sector copying
  4254. routine is called.  The formula given in the previous article is used.
  4255.  
  4256. % readCluster = *  ;( .AY=clusterNumber ) : clusterBuf, .CS=err
  4257. %    jsr convertClusterNum
  4258. %
  4259. %    ;** read logical blocks comprising cluster
  4260. %    ldx #<clusterBuf
  4261. %    stx 6
  4262. %    ldx #>clusterBuf
  4263. %    stx 7
  4264. %    ldx clusterBlockCount
  4265. %    jmp copyBlocks
  4266. %
  4267.  
  4268. Write a logical block out to disk.  The real purpose of this routine is to
  4269. invalidate the read-track cache if the block to be written is contained in
  4270. the cache.
  4271.  
  4272. % writeLogicalBlock = *  ;( .AY=logicalBlockNumber, bufptr ) : .CS=err
  4273. %    jsr convertLogicalBlockNum
  4274. %    cmp bufCylinder
  4275. %    bne +
  4276. %    cpx bufSide
  4277. %    bne +
  4278. %    pha
  4279. %    lda #$ff
  4280. %    sta bufCylinder
  4281. %    sta bufSide
  4282. %    pla
  4283. % +  jsr writeSector
  4284. %    rts
  4285. %
  4286. % writeClusterSave .buf 2
  4287. %
  4288.  
  4289. Write a cluster-ful of data out to disk from the cluster buffer.  This routine
  4290. simply calls the write logical block routine once or twice, depending on the
  4291. cluster size of the disk involved.
  4292.  
  4293. % writeCluster = *  ;( .AY=clusterNumber, clusterBuf ) : .CS=err
  4294. %    jsr convertClusterNum
  4295. %    ldx #<clusterBuf
  4296. %    stx bufptr
  4297. %    ldx #>clusterBuf
  4298. %    stx bufptr+1
  4299. %    sta writeClusterSave
  4300. %    sty writeClusterSave+1
  4301. %    jsr writeLogicalBlock
  4302. %    bcc +
  4303. %    rts
  4304. % +  lda clusterBlockCount
  4305. %    cmp #2
  4306. %    bcs +
  4307. %    rts
  4308. % +  lda writeClusterSave
  4309. %    ldy writeClusterSave+1
  4310. %    clc
  4311. %    adc #1
  4312. %    bcc +
  4313. %    iny
  4314. % +  jsr writeLogicalBlock
  4315. %    rts
  4316. %
  4317.  
  4318. This next level of routines deal with the data structures of the MS-DOS disk
  4319. format.
  4320.  
  4321. % ;====MS-DOS format level====
  4322. %
  4323. % bootBlock = 2
  4324. %
  4325.  
  4326. Read the disk format parameters, directory, and FAT into memory.
  4327.  
  4328. % msDir = *  ;( ) : .AY=dirbuf, .X=dirEntries, .CS=err
  4329. %    lda #$0e
  4330. %    sta $ff00
  4331. %
  4332.  
  4333. Read the boot sector and extract the parameters.
  4334.  
  4335. %    ;** get parameters from boot sector
  4336. %    lda #0
  4337. %    ldy #0
  4338. %    jsr convertLogicalBlockNum
  4339. %    jsr readBlock
  4340. %    bcc +
  4341. %    rts
  4342. % +  sta bootBlock
  4343. %    sty bootBlock+1
  4344. %    ldy #13              ;get cluster size
  4345. %    lda (bootBlock),y
  4346. %    sta clusterBlockCount
  4347. %    cmp #3
  4348. %    bcc +
  4349. %
  4350.  
  4351. If a disk parameter is found to exceed the limits of LRR, error code #60 is
  4352. returned.
  4353.  
  4354. %    invalidParms = *
  4355. %    lda #60
  4356. %    sta errno
  4357. %    sec
  4358. %    rts
  4359. %
  4360. % +  ldy #16              ;check FAT replication count, must be 2
  4361. %    lda (bootBlock),y
  4362. %    cmp #2
  4363. %    bne invalidParms
  4364. %    ldy #22              ;get FAT size in sectors
  4365. %    lda (bootBlock),y
  4366. %    sta fatBlocks
  4367. %    cmp #4
  4368. %    bcs invalidParms
  4369. %    ldy #17              ;get directory size
  4370. %    lda (bootBlock),y
  4371. %    sta rootDirEntries
  4372. %    cmp #129
  4373. %    bcs invalidParms
  4374. %    lsr
  4375. %    lsr
  4376. %    lsr
  4377. %    lsr
  4378. %    sta rootDirBlocks
  4379. %    ldy #19              ;get total sector count
  4380. %    lda (bootBlock),y
  4381. %    sta totalSectors
  4382. %    iny
  4383. %    lda (bootBlock),y
  4384. %    sta totalSectors+1
  4385. %    ldy #24              ;check sectors per track, must be 9
  4386. %    lda (bootBlock),y
  4387. %    cmp #9
  4388. %    bne invalidParms
  4389. %    ldy #26
  4390. %    lda (bootBlock),y
  4391. %    cmp #2               ;check number of sides, must be 2
  4392. %    bne invalidParms
  4393. %    ldy #14              ;check number of boot sectors, must be 1
  4394. %    lda (bootBlock),y
  4395. %    cmp #1
  4396. %    bne invalidParms
  4397. %
  4398.  
  4399. Calculate the derived parameters.
  4400.  
  4401. %    ;** get derived parameters
  4402. %    lda fatBlocks        ;first root directory sector
  4403. %    asl
  4404. %    clc
  4405. %    adc #1
  4406. %    sta firstRootDirBlock
  4407. %    clc                  ;first file sector
  4408. %    adc rootDirBlocks
  4409. %    sta firstFileBlock
  4410. %    lda totalSectors     ;number of file clusters
  4411. %    ldy totalSectors+1
  4412. %    sec
  4413. %    sbc firstFileBlock
  4414. %    bcs +
  4415. %    dey
  4416. % +  sta fileClusterCount
  4417. %    sty fileClusterCount+1
  4418. %    lda clusterBlockCount
  4419. %    cmp #2
  4420. %    bne +
  4421. %    lsr fileClusterCount+1
  4422. %    ror fileClusterCount
  4423. % +  clc
  4424. %    lda fileClusterCount
  4425. %    adc #2
  4426. %    sta lastFatEntry
  4427. %    lda fileClusterCount+1
  4428. %    adc #0
  4429. %    sta lastFatEntry+1
  4430. %
  4431. %    ;** load FAT
  4432. %    lda #<fatbuf
  4433. %    ldy #>fatbuf
  4434. %    sta 6
  4435. %    sty 7
  4436. %    lda #1
  4437. %    ldy #0
  4438. %    ldx fatBlocks
  4439. %    jsr copyBlocks
  4440. %    bcc +
  4441. %    rts
  4442. %
  4443. %    ;** load actual directory
  4444. % +  lda #<dirbuf
  4445. %    ldy #>dirbuf
  4446. %    sta 6
  4447. %    sty 7
  4448. %    lda firstRootDirBlock
  4449. %    ldy #0
  4450. %    ldx rootDirBlocks
  4451. %    jsr copyBlocks
  4452. %    bcc +
  4453. %    rts
  4454. % +  lda #<dirbuf
  4455. %    ldy #>dirbuf
  4456. %    ldx rootDirEntries
  4457. %    clc
  4458. %    rts
  4459. %
  4460.  
  4461. This routine locates the given FAT table entry number and returns the value
  4462. stored in it.  Some work is needed to deal with the 12-bit compressed data
  4463. structure.
  4464.  
  4465. % entryAddr = 2
  4466. % entryWork = 4
  4467. % entryBits = 5
  4468. % entryData0 = 6
  4469. % entryData1 = 7
  4470. % entryData2 = 8
  4471. %
  4472. % locateFatEntry = *  ;( .AY=fatEntryNumber ) : entryAddr, entryBits%1
  4473.  
  4474. Divide the FAT entry number by two and multiply by three because two FAT
  4475. entries are stored in three bytes.  Then add the FAT base address and we have
  4476. the address of the three bytes that contain the FAT entry we are interested
  4477. in.  I retrieve the three bytes into zero-page memory for easy manipulation.
  4478.  
  4479. %    sta entryBits
  4480. %    ;** divide by two
  4481. %    sty entryAddr+1
  4482. %    lsr entryAddr+1
  4483. %    ror
  4484. %
  4485. %    ;** times three
  4486. %    sta entryWork
  4487. %    ldx entryAddr+1
  4488. %    asl
  4489. %    rol entryAddr+1
  4490. %    clc
  4491. %    adc entryWork
  4492. %    sta entryAddr
  4493. %    txa
  4494. %    adc entryAddr+1
  4495. %    sta entryAddr+1
  4496. %
  4497. %    ;** add base, get data
  4498. %    clc
  4499. %    lda entryAddr
  4500. %    adc #<fatbuf
  4501. %    sta entryAddr
  4502. %    lda entryAddr+1
  4503. %    adc #>fatbuf
  4504. %    sta entryAddr+1
  4505. %    ldy #2
  4506. % -  lda (entryAddr),y
  4507. %    sta entryData0,y
  4508. %    dey
  4509. %    bpl -
  4510. %    rts
  4511. %
  4512. % getFatEntry = *  ;( .AY=fatEntryNumber ) : .AY=fatEntryValue
  4513. %    jsr locateFatEntry
  4514. %    lda entryBits
  4515. %    and #1
  4516. %    bne +
  4517. %
  4518.  
  4519. If the original given FAT entry number is even, then we want the first 12-bit
  4520. compressed field.  The nybbles are extracted according to the diagram shown
  4521. earlier.
  4522.  
  4523. %    ;** case 1: first 12-bit cluster
  4524. %    lda entryData1
  4525. %    and #$0f
  4526. %    tay
  4527. %    lda entryData0
  4528. %    rts
  4529. %
  4530.  
  4531. Otherwise, we want the second 12-bit field.
  4532.  
  4533. %    ;** case 2: second 12-bit cluster
  4534. % +  lda entryData1
  4535. %    ldx #4
  4536. % -  lsr entryData2
  4537. %    ror
  4538. %    dex
  4539. %    bne -
  4540. %    ldy entryData2
  4541. %    rts
  4542. %
  4543. % fatValue = 9
  4544. %
  4545.  
  4546. Change the value in a FAT entry.  This routine is quite similar to the get
  4547. routine.
  4548.  
  4549. % setFatEntry = *  ;( .AY=fatEntryNumber, (fatValue) )
  4550. %    jsr locateFatEntry
  4551. %    lda fatValue+1
  4552. %    and #$0f
  4553. %    sta fatValue+1
  4554. %    lda entryBits
  4555. %    and #1
  4556. %    bne +
  4557. %
  4558. %    ;** case 1: first 12-bit cluster
  4559. %    lda fatValue
  4560. %    sta entryData0
  4561. %    lda entryData1
  4562. %    and #$f0
  4563. %    ora fatValue+1
  4564. %    sta entryData1
  4565. %    jmp setFatExit
  4566. %
  4567. %    ;** case 2: second 12-bit cluster
  4568. % +  ldx #4
  4569. % -  asl fatValue
  4570. %    rol fatValue+1
  4571. %    dex
  4572. %    bne -
  4573. %    lda fatValue+1
  4574. %    sta entryData2
  4575. %    lda entryData1
  4576. %    and #$0f
  4577. %    ora fatValue
  4578. %    sta entryData1
  4579. %
  4580. %    setFatExit = *
  4581. %    ldy #2
  4582. % -  lda entryData0,y
  4583. %    sta (entryAddr),y
  4584. %    dey
  4585. %    bpl -
  4586. %    sty fatDirty
  4587. %    rts
  4588. %
  4589.  
  4590. Mark the directory sector corresponding to the given directory entry as being
  4591. dirty so it will be written out to disk the next time the msFlush routine is
  4592. called.
  4593.  
  4594. % dirtyDirent = *  ;( writeDirent )
  4595. %    sec
  4596. %    lda writeDirent
  4597. %    sbc #<dirbuf
  4598. %    lda writeDirent+1
  4599. %    sbc #>dirbuf
  4600. %    lsr
  4601. %    and #$07
  4602. %    tax
  4603. %    lda #$ff
  4604. %    sta dirDirty,x
  4605. %    rts
  4606. %
  4607. % delCluster = 14
  4608. %
  4609.  
  4610. Delete the MS-DOS file whose directory entry is given.  Put the $E5 into
  4611. its filename, get its starting cluster and follow the chain of clusters
  4612. allocated to the file in the FAT, marking them as unallocated (value $000)
  4613. as we go.  Exit by marking the directory entry as "dirty".
  4614.  
  4615. % msDelete = *  ;( writeDirent )
  4616. %    ldy #$0e
  4617. %    sty $ff00
  4618. %    lda writeDirent
  4619. %    ldy writeDirent+1
  4620. %    sta 2
  4621. %    sty 3
  4622. %    lda #$e5
  4623. %    ldy #0
  4624. %    sta (2),y
  4625. %    ldy #26
  4626. %    lda (2),y
  4627. %    sta delCluster
  4628. %    iny
  4629. %    lda (2),y
  4630. %    sta delCluster+1
  4631. % -  lda delCluster+1
  4632. %    cmp #5
  4633. %    bcc +
  4634. %    jmp dirtyDirent
  4635. % +  tay
  4636. %    lda delCluster
  4637. %    jsr getFatEntry
  4638. %    pha
  4639. %    tya
  4640. %    pha
  4641. %    lda #0
  4642. %    sta fatValue
  4643. %    sta fatValue+1
  4644. %    lda delCluster
  4645. %    ldy delCluster+1
  4646. %    jsr setFatEntry
  4647. %    pla
  4648. %    sta delCluster+1
  4649. %    pla
  4650. %    sta delCluster
  4651. %    jmp -
  4652. %
  4653. % flushBlock = 14
  4654. % flushCountdown = $60
  4655. % flushRepeats = $61
  4656. % flushDirIndex = $61
  4657. %
  4658.  
  4659. Write the FAT and directory sectors from memory to disk, if they are dirty.
  4660.  
  4661. % msFlush = *  ;( msDevice, msType ) : .CS=error
  4662. %    lda #$0e
  4663. %    sta $ff00
  4664. %    lda fatDirty
  4665. %    beq flushDirectory
  4666. %    lda #0
  4667. %    sta fatDirty
  4668. %
  4669. %    ;** flush fat
  4670.  
  4671. Flush both copies of the FAT, if there are two; otherwise, only flush the one.
  4672.  
  4673. %    lda #2
  4674. %    sta flushRepeats
  4675. %    lda #1
  4676. %    sta flushBlock
  4677. %
  4678. %    masterFlush = *
  4679. %    lda fatBlocks
  4680. %    sta flushCountdown
  4681. %    lda #<fatbuf
  4682. %    ldy #>fatbuf
  4683. %    sta bufptr
  4684. %    sty bufptr+1
  4685. % -  lda flushBlock
  4686. %    ldy #0
  4687. %    jsr writeLogicalBlock
  4688. %    bcc +
  4689. %    rts
  4690. % +  inc flushBlock
  4691. %    dec flushCountdown
  4692. %    bne -
  4693. %    dec flushRepeats
  4694. %    bne masterFlush
  4695. %
  4696. %    ;** flush directory
  4697. %    flushDirectory = *
  4698. %    lda firstRootDirBlock
  4699. %    sta flushBlock
  4700. %    lda rootDirBlocks
  4701. %    sta flushCountdown
  4702. %    lda #0
  4703. %    sta flushDirIndex
  4704. %    lda #<dirbuf
  4705. %    ldy #>dirbuf
  4706. %    sta bufptr
  4707. %    sty bufptr+1
  4708. % -  ldx flushDirIndex
  4709. %    lda dirDirty,x
  4710. %    beq +
  4711. %    lda #0
  4712. %    sta dirDirty,x
  4713. %    lda flushBlock
  4714. %    ldy #0
  4715. %    jsr writeLogicalBlock
  4716. %    dec bufptr+1
  4717. %    dec bufptr+1
  4718. % +  inc flushBlock
  4719. %    inc flushDirIndex
  4720. %    inc bufptr+1
  4721. %    inc bufptr+1
  4722. %    dec flushCountdown
  4723. %    bne -
  4724. %    clc
  4725. %    rts
  4726. %
  4727. % bfFatEntry = 14
  4728. % bfBlocks = $60
  4729. %
  4730.  
  4731. Count the number of free FAT entries (value $000) from entry 2 up to the
  4732. highest FAT entry available for cluster allocation.  Then multiply this
  4733. by the number of bytes per cluster (either 512 or 1024).
  4734.  
  4735. % msBytesFree = *  ;( ) : .AYX=fileBytesFree
  4736. %    ldy #$0e
  4737. %    sty $ff00
  4738. %    lda #2
  4739. %    ldy #0
  4740. %    sta bfFatEntry
  4741. %    sty bfFatEntry+1
  4742. %    sty bfBlocks
  4743. %    sty bfBlocks+1
  4744. % -  lda bfFatEntry
  4745. %    ldy bfFatEntry+1
  4746. %    jsr getFatEntry
  4747. %    sty 2
  4748. %    ora 2
  4749. %    bne +
  4750. %    inc bfBlocks
  4751. %    bne +
  4752. %    inc bfBlocks+1
  4753. % +  inc bfFatEntry
  4754. %    bne +
  4755. %    inc bfFatEntry+1
  4756. % +  lda bfFatEntry
  4757. %    cmp lastFatEntry
  4758. %    lda bfFatEntry+1
  4759. %    sbc lastFatEntry+1
  4760. %    bcc -
  4761. %    ldx clusterBlockCount
  4762. % -  asl bfBlocks
  4763. %    rol bfBlocks+1
  4764. %    dex
  4765. %    bne -
  4766. %    lda #0
  4767. %    ldy bfBlocks
  4768. %    ldx bfBlocks+1
  4769. %    rts
  4770. %
  4771.  
  4772. This is the file copying level.  It deals with reading/writing the clusters of
  4773. MS-DOS files and copying the data they contain to/from the already-open CBM
  4774. Kernal file, possibly with ASCII/PETSCII translation.
  4775.  
  4776. % ;====file copy level====
  4777. %
  4778. % transMode = 14
  4779. % lfn = 15
  4780. % cbmDataPtr = $60
  4781. % cbmDataLen = $62
  4782. % cluster = $64
  4783. %
  4784.  
  4785. Copy the given cluster to the CBM output file.  This routine fetches the next
  4786. cluster of the file for the next time this routine is called, and if it hits
  4787. the NULL pointer of the last cluster of a file, it adjusts the number of valid
  4788. file data bytes the current cluster contains to FileLength % ClusterLength
  4789. (see note below).
  4790.  
  4791. % copyFileCluster = *  ;( cluster, lfn, transMode ) : .CS=err
  4792.  
  4793. Read the cluster and setup to copy the whole cluster to the CBM file.
  4794.  
  4795. %    lda cluster
  4796. %    ldy cluster+1
  4797. %    jsr readCluster
  4798. %    bcc +
  4799. %    rts
  4800. % +  lda #<clusterBuf
  4801. %    ldy #>clusterBuf
  4802. %    sta cbmDataPtr
  4803. %    sty cbmDataPtr+1
  4804. %    lda #0
  4805. %    sta cbmDataLen
  4806. %    lda clusterBlockCount
  4807. %    asl
  4808. %    sta cbmDataLen+1
  4809. %
  4810.  
  4811. Fetch the next cluster number of the file, and adjust the cluster data length
  4812. for the last cluster of the file.
  4813.  
  4814. %    ;**get next cluster
  4815. %    lda cluster
  4816. %    ldy cluster+1
  4817. %    jsr getFatEntry
  4818. %    sta cluster
  4819. %    sty cluster+1
  4820. %    cpy #$05
  4821. %    bcc copyFileClusterData
  4822. %    lda lenML
  4823. %    sta cbmDataLen
  4824. %    lda #$01
  4825. %    ldx clusterBlockCount
  4826. %    cpx #1
  4827. %    beq +
  4828. %    lda #$03
  4829. % +  and lenML+1
  4830.  
  4831. The following three lines were added in a last minute panic after realizing
  4832. that if FileLength % ClusterSize == 0, then the last cluster of the file
  4833. contains ClusterSize bytes, not zero bytes.
  4834.  
  4835. %    bne +
  4836. %    ldx lenML
  4837. %    beq copyFileClusterData
  4838. % +  sta cbmDataLen+1
  4839. %
  4840. %    copyFileClusterData = *
  4841. %    jsr commieOut
  4842. %    rts
  4843. %
  4844.  
  4845. Copy the file data in the MS-DOS cluster buffer to the CBM output file.
  4846.  
  4847. % cbmDataLimit = $66
  4848. %
  4849. % commieOut = *  ;( cbmDataPtr, cbmDataLen ) : .CS=err
  4850.  
  4851. If the the logical file number to copy to is 0 ("null device"), then don't
  4852. bother copying anything.
  4853.  
  4854. %    ldx lfn
  4855. %    bne +
  4856. %    clc
  4857. %    rts
  4858.  
  4859. Otherwise, prepare the logical file number for output.
  4860.  
  4861. % +  jsr kernelChkout
  4862. %    bcc commieOutMore
  4863. %    sta errno
  4864. %    rts
  4865. %
  4866.  
  4867. Process the cluster data in chunks of up to 255 bytes or the number of data
  4868. bytes remaining in the cluster.
  4869.  
  4870. %    commieOutMore = *
  4871. %    lda #255
  4872. %    ldx cbmDataLen+1
  4873. %    bne +
  4874. %    lda cbmDataLen
  4875. % +  sta cbmDataLimit
  4876. %    ldy #0
  4877. % -  lda (cbmDataPtr),y
  4878. %    bit transMode
  4879. %    bpl +
  4880.  
  4881. If we have to translate the current ASCII character, look up the PETSCII value
  4882. in the translation table and output that value.  If the translation table
  4883. entry value is $00, then don't output a character (filter out invalid
  4884. character codes).
  4885.  
  4886. %    tax
  4887. %    lda transBuf,x
  4888. %    beq commieNext
  4889. % +  jsr kernelChrout
  4890. %    commieNext = *
  4891. %    iny
  4892. %    cpy cbmDataLimit
  4893. %    bne -
  4894. %
  4895.  
  4896. Increment the cluster buffer pointer and decrement the cluster buffer character
  4897. count according to the number of bytes just processed, and repeat the above if
  4898. more file data remains in the current cluster.
  4899.  
  4900. %    clc
  4901. %    lda cbmDataPtr
  4902. %    adc cbmDataLimit
  4903. %    sta cbmDataPtr
  4904. %    bcc +
  4905. %    inc cbmDataPtr+1
  4906. % +  sec
  4907. %    lda cbmDataLen
  4908. %    sbc cbmDataLimit
  4909. %    sta cbmDataLen
  4910. %    bcs +
  4911. %    dec cbmDataLen+1
  4912. % +  lda cbmDataLen
  4913. %    ora cbmDataLen+1
  4914. %    bne commieOutMore
  4915.  
  4916. If we are finished with the cluster, then clear the CBM Kernal output channel.
  4917.  
  4918. %    jsr kernelClrchn
  4919. %    clc
  4920. %    rts
  4921. %
  4922.  
  4923. The file copying main routine.  Set up for the starting cluster, and call
  4924. the cluster copying routine until end-of-file is reached.  Checks for a
  4925. NULL cluster pointer in the directory entry to handle zero-length files.
  4926.  
  4927. % msRead = *  ;( cluster, lenML, .A=transMode, .X=lfn ) : .CS=err
  4928. %    ldy #$0e
  4929. %    sty $ff00
  4930. %    sta transMode
  4931. %    stx lfn
  4932. %    lda startCluster
  4933. %    ldy startCluster+1
  4934. %    sta cluster
  4935. %    sty cluster+1
  4936. %    jmp +
  4937. % -  jsr copyFileCluster
  4938. %    bcc +
  4939. %    rts
  4940. % +  lda cluster+1
  4941. %    cmp #$05
  4942. %    bcc -
  4943. %    clc
  4944. %    rts
  4945. %
  4946. % inLfn = $50
  4947. % generateLf = $51
  4948. % cbmDataMax = $52
  4949. % reachedEof = $54
  4950. % prevSt = $55
  4951. %
  4952.  
  4953. Set the translation and input logical file number and set up for reading
  4954. from a CBM-Kernal input file.
  4955.  
  4956. % commieInInit = *  ;( .A=transMode, .X=inLfn )
  4957. %    sta transMode
  4958. %    stx inLfn
  4959. %    lda #0
  4960. %    sta generateLf
  4961. %    sta reachedEof
  4962. %    sta prevSt
  4963. %    rts
  4964. %
  4965.  
  4966. Read up to "cbmDataMax" bytes into the specified buffer from the established
  4967. CBM logical file number.  The number of bytes read is returned in
  4968. "cbmDataLen".  If end of file occurs, "cbmDataLen" will be zero and the .Z
  4969. flag will be set.  Regular error return.
  4970.  
  4971. % commieIn = *  ;( cbmDataPtr++, cbmDataMax ) : cbmDataLen, .CS=err, .Z=eof
  4972.  
  4973. Establish input file, or return immediately if already past eof.
  4974.  
  4975. %    lda #0
  4976. %    sta cbmDataLen
  4977. %    sta cbmDataLen+1
  4978. %    ldx reachedEof
  4979. %    beq +
  4980. %    lda #0
  4981. %    clc
  4982. %    rts
  4983. % +  ldx inLfn
  4984. %    jsr kernelChkin
  4985. %    bcc commieInMore
  4986. %    sta errno
  4987. %    rts
  4988. %
  4989.  
  4990. Read next chunk of up to 255 bytes into input buffer.
  4991.  
  4992. %    commieInMore = *
  4993. %    lda #255
  4994. %    ldx cbmDataMax+1
  4995. %    bne +
  4996. %    lda cbmDataMax
  4997. % +  sta cbmDataLimit
  4998. %    ldy #0
  4999. % -  jsr commieInByte
  5000. %    bcc +
  5001. %    rts
  5002. % +  beq +
  5003. %    sta (cbmDataPtr),y
  5004. %    iny
  5005. %    cpy cbmDataLimit
  5006. %    bne -
  5007. %
  5008.  
  5009. Prepare to read another chunk, or exit.
  5010.  
  5011. % +  sty cbmDataLimit
  5012. %    clc
  5013. %    lda cbmDataPtr
  5014. %    adc cbmDataLimit
  5015. %    sta cbmDataPtr
  5016. %    bcc +
  5017. %    inc cbmDataPtr+1
  5018. % +  clc
  5019. %    lda cbmDataLen
  5020. %    adc cbmDataLimit
  5021. %    sta cbmDataLen
  5022. %    bcc +
  5023. %    inc cbmDataLen+1
  5024. % +  sec
  5025. %    lda cbmDataMax
  5026. %    sbc cbmDataLimit
  5027. %    sta cbmDataMax
  5028. %    bcs +
  5029. %    dec cbmDataMax+1
  5030. % +  lda reachedEof
  5031. %    bne +
  5032. %    lda cbmDataMax
  5033. %    ora cbmDataMax+1
  5034. %    bne commieInMore
  5035.  
  5036. Shut down reading and exit.
  5037.  
  5038. % +  jsr kernelClrchn
  5039. %    lda cbmDataLen
  5040. %    ora cbmDataLen+1
  5041. %    clc
  5042. %    rts
  5043. %
  5044.  
  5045. Read a single byte from the CBM-Kernal input logical file number.  Translate
  5046. character into ASCII and expand CR into CR+LF if necessary.  Return EOF if
  5047. previous character returned was last from disk input channel.
  5048.  
  5049. % commieInByte = *  ;( ) : .A=char, .CS=err, .Z=eof, reachedEof
  5050. %    ;** check for already past eof
  5051. %    lda reachedEof
  5052. %    beq +
  5053. %    brk
  5054. %    ;** check for generated linefeed
  5055. % +  lda generateLf
  5056. %    beq +
  5057. %    lda #0
  5058. %    sta generateLf
  5059. %    lda #$0a
  5060. %    clc
  5061. %    rts
  5062. %    ;** check for eof
  5063. % +  lda prevSt
  5064. %    and #$40
  5065. %    beq +
  5066. %    lda #$ff
  5067. %    sta reachedEof
  5068. %    lda #0
  5069. %    clc
  5070. %    rts
  5071. %    ;** read actual character
  5072. % +  jsr kernelChrin
  5073. %    ldx st
  5074. %    stx prevSt
  5075. %    bcc +
  5076. %    sta errno
  5077. %    jsr kernelClrchn
  5078. %    rts
  5079. %    ;** translate if necessary
  5080. % +  bit transMode
  5081. %    bpl +
  5082. %    tax
  5083. %    lda transBufToAscii,x
  5084. %    beq commieInByte
  5085.  
  5086. Note here that the translated character is checked to see if it is a carriage
  5087. return, rather than checking the non-translated character, to see if a
  5088. linefeed must be generated next.  Thus, you could define that a Commodore
  5089. carriage return be translated into a linefeed (for Unix) and no additional
  5090. unwanted linefeed would be generated.
  5091.  
  5092. %    cmp #$0d
  5093. %    bne +
  5094. %    sta generateLf
  5095. %    ;** exit
  5096. % +  ldx #$ff
  5097. %    clc
  5098. %    rts
  5099. %
  5100. % firstFreeFatEntry = $5a
  5101. %
  5102.  
  5103. Search FAT for a free cluster, and return the cluster (FAT entry) number.  A
  5104. global variable "firstFreeFarEntry" is maintained which points to the first
  5105. FAT entry that could possibly be free, to avoid wasting time searching from
  5106. the very beginning of the FAT every time.  Clusters are allocated in
  5107. first-free order.
  5108.  
  5109. % allocateFatEntry = *  ;( ) : .AY=fatEntry, .CS=err
  5110. % -  lda firstFreeFatEntry
  5111. %    cmp lastFatEntry
  5112. %    lda firstFreeFatEntry+1
  5113. %    sbc lastFatEntry+1
  5114. %    bcc +
  5115. %    rts
  5116. % +  lda firstFreeFatEntry
  5117. %    ldy firstFreeFatEntry+1
  5118. %    jsr getFatEntry
  5119. %    sty 2
  5120. %    ora 2
  5121. %    bne +
  5122. %    lda firstFreeFatEntry
  5123. %    ldy firstFreeFatEntry+1
  5124. %    clc
  5125. %    rts
  5126. % +  inc firstFreeFatEntry
  5127. %    bne -
  5128. %    inc firstFreeFatEntry+1
  5129. %    jmp -
  5130. %
  5131. % msFileLength = $5c  ;(3 bytes)
  5132. %
  5133.  
  5134. Allocate a new cluster to a file, link it into the file cluster chain, and
  5135. write the cluster buffer to disk in that cluster, adding "cbmDataLen" bytes
  5136. to the file.
  5137.  
  5138. % msWriteCluster = *  ; (*) : .CS=err
  5139. %    ;** get a new cluster
  5140. %    jsr allocateFatEntry
  5141. %    bcc +
  5142. %    rts
  5143. %    ;** make previous fat entry point to new cluster
  5144. % +  sta fatValue
  5145. %    sty fatValue+1
  5146. %    lda cluster
  5147. %    ora cluster+1
  5148. %    beq +
  5149. %    lda cluster
  5150. %    ldy cluster+1
  5151. %    ldx fatValue
  5152. %    stx cluster
  5153. %    ldx fatValue+1
  5154. %    stx cluster+1
  5155. %    jsr setFatEntry
  5156. %    jmp msClusterNew
  5157.  
  5158. Handle case of no previous cluster - make directory entry point to new
  5159. cluster.
  5160.  
  5161. % +  lda writeDirent
  5162. %    ldy writeDirent+1
  5163. %    sta 2
  5164. %    sty 3
  5165. %    ldy #26
  5166. %    lda fatValue
  5167. %    sta (2),y
  5168. %    sta cluster
  5169. %    iny
  5170. %    lda fatValue+1
  5171. %    sta (2),y
  5172. %    sta cluster+1
  5173. %
  5174. %    ;** make new fat entry point to null
  5175. %    msClusterNew = *
  5176. %    lda #$ff
  5177. %    ldy #$0f
  5178. %    sta fatValue
  5179. %    sty fatValue+1
  5180. %    lda cluster
  5181. %    ldy cluster+1
  5182. %    jsr setFatEntry
  5183. %    ;** write new cluster data
  5184. % +  lda cluster
  5185. %    ldy cluster+1
  5186. %    jsr writeCluster
  5187. %    bcc +
  5188. %    rts
  5189. %    ;** add cluster length to file length
  5190. % +  clc
  5191. %    lda msFileLength
  5192. %    adc cbmDataLen
  5193. %    sta msFileLength
  5194. %    lda msFileLength+1
  5195. %    adc cbmDataLen+1
  5196. %    sta msFileLength+1
  5197. %    bcc +
  5198. %    inc msFileLength+2
  5199. % +  clc
  5200. %    rts
  5201. %
  5202.  
  5203. Copy a CBM-Kernal file to an MS-DOS file, possibly with translation.
  5204.  
  5205. % msWrite = *  ;( msDevice, msType, writeDirent, .A=trans, .X=cbmLfn ) :.CS=err
  5206. %    ldy #$0e
  5207. %    sty $ff00
  5208. %    ;** initialize
  5209.  
  5210. Set input file translation and logical file number, init cluster, file length,
  5211. FAT allocation first free pointer (to cluster #2, the first data cluster).
  5212.  
  5213. %    jsr commieInInit
  5214. %    lda #0
  5215. %    sta cluster
  5216. %    sta cluster+1
  5217. %    sta firstFreeFatEntry+1
  5218. %    sta msFileLength
  5219. %    sta msFileLength+1
  5220. %    sta msFileLength+2
  5221. %    lda #2
  5222. %    sta firstFreeFatEntry
  5223. %
  5224. %    ;** copy cluster from cbm file
  5225. % -  lda #<clusterBuf
  5226. %    ldy #>clusterBuf
  5227. %    sta cbmDataPtr
  5228. %    sty cbmDataPtr+1
  5229. %    lda clusterBlockCount
  5230. %    asl
  5231. %    tay
  5232. %    lda #0
  5233. %    sta cbmDataMax
  5234. %    sty cbmDataMax+1
  5235. %    jsr commieIn
  5236. %    bcc +
  5237. %    rts
  5238. % +  beq +
  5239. %    jsr msWriteCluster
  5240. %    bcc -
  5241. %    rts
  5242. %
  5243. %    ;** wrap up after writing - set file length, dirty flag, exit.
  5244. % +  lda writeDirent
  5245. %    ldy writeDirent+1
  5246. %    sta 2
  5247. %    sty 3
  5248. %    ldx #0
  5249. %    ldy #28
  5250. % -  lda msFileLength,x
  5251. %    sta (2),y
  5252. %    iny
  5253. %    inx
  5254. %    cpx #3
  5255. %    bcc -
  5256. %    jsr dirtyDirent
  5257. %    clc
  5258. %    rts
  5259. %
  5260.  
  5261. This level deals exclusively with Commodore files.
  5262.  
  5263. % ;===== commodore file level =====
  5264. %
  5265.  
  5266. Copy from an input disk logical file number to an output lfn, in up to 1024
  5267. byte chunks.  This routine makes use of the existing "commieIn" and
  5268. "commieOut" routines.  No file translation is available; binary translation is
  5269. used for both commieIn and commieOut.
  5270.  
  5271. % cbmCopy = *  ;( .A=inLfn, .X=outLfn )
  5272. %    ldy #$0e
  5273. %    sty $ff00
  5274. %    stx lfn
  5275. %    tax
  5276. %    lda #0
  5277. %    jsr commieInInit
  5278. % -  lda #<clusterBuf
  5279. %    ldy #>clusterBuf
  5280. %    sta cbmDataPtr
  5281. %    sty cbmDataPtr+1
  5282. %    lda #<1024
  5283. %    ldy #>1024
  5284. %    sta cbmDataMax
  5285. %    sty cbmDataMax+1
  5286. %    jsr commieIn
  5287. %    bcs +
  5288. %    beq +
  5289. %    lda #<clusterBuf
  5290. %    ldy #>clusterBuf
  5291. %    sta cbmDataPtr
  5292. %    sty cbmDataPtr+1
  5293. %    jsr commieOut
  5294. %    bcs +
  5295. %    jmp -
  5296. % +  rts
  5297. %
  5298.  
  5299. Read a single directory entry from the given logical file number, which is
  5300. assumed to be open for reading a directory ("$").  The data of the directory
  5301. entry are returned in the interface variables.
  5302.  
  5303. % cbmDirent = *  ;( .A=lfn )
  5304.  
  5305. Initialize.
  5306.  
  5307. %    ldy #$0e
  5308. %    sty $ff00
  5309. %    tax
  5310. %    jsr kernelChkin
  5311. %    bcc +
  5312. %    cdirErr = *
  5313. %    lda #0
  5314. %    sta cdirFlen
  5315. %    sta cdirBlocks
  5316. %    sta cdirBlocks+1
  5317. %    rts
  5318. %    ;** get block count
  5319. % +  jsr cdirGetch
  5320. %    jsr cdirGetch
  5321. %    jsr cdirGetch
  5322. %    sta cdirBlocks
  5323. %    jsr cdirGetch
  5324. %    sta cdirBlocks+1
  5325. %    ;** look for filename
  5326. %    lda #0
  5327. %    sta cdirFlen
  5328. % -  jsr cdirGetch
  5329. %    cmp #34
  5330. %    beq +
  5331. %    cmp #"b"
  5332. %    bne -
  5333. %    jsr kernelClrchn
  5334. %    rts
  5335. %    ;** get filename
  5336. % +  ldy #0
  5337. % -  jsr cdirGetch
  5338. %    cmp #34
  5339. %    beq +
  5340. %    sta cdirName,y
  5341. %    iny
  5342. %    bne -
  5343. % +  sty cdirFlen
  5344.  
  5345. Look for and get file type.
  5346.  
  5347. % -  jsr cdirGetch
  5348. %    cmp #" "
  5349. %    beq -
  5350. %    sta cdirType
  5351.  
  5352. Scan for end of directory entry, return.
  5353.  
  5354. % -  jsr cdirGetch
  5355. %    cmp #0
  5356. %    bne -
  5357. %    jsr kernelClrchn
  5358. %    rts
  5359. %
  5360.  
  5361. Get a single character of the directory entry, watching for end of file (which
  5362. would indicate error here).
  5363.  
  5364. % cdirGetch = *
  5365. %    jsr kernelChrin
  5366. %    bcs +
  5367. %    bit st
  5368. %    bvs +
  5369. %    rts
  5370. % +  pla
  5371. %    pla
  5372. %    jsr kernelClrchn
  5373. %    jmp cdirErr
  5374. %
  5375. % ;===== data =====
  5376. %
  5377.  
  5378. This is the translation table used to convert from ASCII to PETSCII.  You can
  5379. modify it to suit your needs if you wish.  If you cannot reassemble this file,
  5380. then you can sift through the binary file and locate the table and change it
  5381. there.  An entry of $00 means the corresponding ASCII character will not be
  5382. translated.  You'll notice that I have set up translations for the following
  5383. ASCII control characters into PETSCII: Backspace, Tab, Linefeed (CR), and
  5384. Formfeed.  I also translate the non-PETSCII characters such as {, |, ~, and _
  5385. according to what they probably would have been if Commodore wasn't so
  5386. concerned with the graphics characters.
  5387.  
  5388. % transBuf = *
  5389. %        ;0   1   2   3   4   5   6   7   8   9   a   b   c   d   e   f
  5390. % .byte $00,$00,$00,$00,$00,$00,$00,$00,$14,$09,$0d,$00,$93,$00,$00,$00 ;0
  5391. % .byte $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 ;1
  5392. % .byte $20,$21,$22,$23,$24,$25,$26,$27,$28,$29,$2a,$2b,$2c,$2d,$2e,$2f ;2
  5393. % .byte $30,$31,$32,$33,$34,$35,$36,$37,$38,$39,$3a,$3b,$3c,$3d,$3e,$3f ;3
  5394. % .byte $40,$c1,$c2,$c3,$c4,$c5,$c6,$c7,$c8,$c9,$ca,$cb,$cc,$cd,$ce,$cf ;4
  5395. % .byte $d0,$d1,$d2,$d3,$d4,$d5,$d6,$d7,$d8,$d9,$da,$5b,$5c,$5d,$5e,$5f ;5
  5396. % .byte $c0,$41,$42,$43,$44,$45,$46,$47,$48,$49,$4a,$4b,$4c,$4d,$4e,$4f ;6
  5397. % .byte $50,$51,$52,$53,$54,$55,$56,$57,$58,$59,$5a,$db,$dc,$dd,$de,$df ;7
  5398. % .byte $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 ;8
  5399. % .byte $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 ;9
  5400. % .byte $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 ;a
  5401. % .byte $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 ;b
  5402. % .byte $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 ;c
  5403. % .byte $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 ;d
  5404. % .byte $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 ;e
  5405. % .byte $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 ;f
  5406. %
  5407.  
  5408. This is the translation table used to convert from PETSCII to ASCII.  You can
  5409. modify it to suit your needs, similar to the ASCII to PETSCII table.  An entry
  5410. of $00 means the corresponding PETSCII character will not be translated.
  5411. You'll notice that I have set up translations for the following PETSCII
  5412. control characters into ASCII: Delete (into Backspace), Tab, Carriage Return
  5413. (into CR+LF), and ClearScreen (into Fordfeed).  Appropriate translations into
  5414. the ASCII characters {, }, ^, _, ~, \, and | are also set up.
  5415.  
  5416. % transBufToAscii = *
  5417. %        ;0   1   2   3   4   5   6   7   8   9   a   b   c   d   e   f
  5418. % .byte $00,$00,$00,$00,$00,$00,$00,$00,$00,$09,$00,$00,$00,$0d,$00,$00 ;0
  5419. % .byte $00,$00,$00,$00,$08,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 ;1
  5420. % .byte $20,$21,$22,$23,$24,$25,$26,$27,$28,$29,$2a,$2b,$2c,$2d,$2e,$2f ;2
  5421. % .byte $30,$31,$32,$33,$34,$35,$36,$37,$38,$39,$3a,$3b,$3c,$3d,$3e,$3f ;3
  5422. % .byte $40,$61,$62,$63,$64,$65,$66,$67,$68,$69,$6a,$6b,$6c,$6d,$6e,$6f ;4
  5423. % .byte $70,$71,$72,$73,$74,$75,$76,$77,$78,$79,$7a,$5b,$5c,$5d,$5e,$5f ;5
  5424. % .byte $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 ;6
  5425. % .byte $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 ;7
  5426. % .byte $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 ;8
  5427. % .byte $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 ;9
  5428. % .byte $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 ;a
  5429. % .byte $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 ;b
  5430. % .byte $60,$41,$42,$43,$44,$45,$46,$47,$48,$49,$4a,$4b,$4c,$4d,$4e,$4f ;c
  5431. % .byte $50,$51,$52,$53,$54,$55,$56,$57,$58,$59,$5a,$7b,$7c,$7d,$7e,$7f ;d
  5432. % .byte $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 ;e
  5433. % .byte $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$7e ;f
  5434. %
  5435. % ;====bss storage (size=11,264 bytes)====
  5436. %
  5437.  
  5438. This is where the track cache, etc. are stored.  This section requires 11K of
  5439. storage space but does not increase the length of the binary program file
  5440. since these storage areas are DEFINED rather than allocated with ".buf"
  5441. directives.  The Unix terminology for this type of uninitialized data is
  5442. "bss".
  5443.  
  5444. % bss = *
  5445. % trackbuf   = bss
  5446. % clusterBuf = trackbuf+4608
  5447. % fatbuf     = clusterBuf+1024
  5448. % dirbuf     = fatbuf+1536
  5449. % end        = dirbuf+4096
  5450.  
  5451. 5. USER-INTERFACE PROGRAM
  5452.  
  5453. This section presents the listing of the user-interface BASIC program.  You
  5454. should be aware that you can easily change some of the defaults to your own
  5455. preferences if you wish.  In particular, you may wish to change the "dv" and
  5456. "dt" variables in lines 25 and 26.  This program is not listed in the "%"
  5457. format that the assembler listing is since you can recover this listing from
  5458. the uuencoded binary program file.  The listing is here in its entirety.
  5459.  
  5460. 10 print chr$(147);"little red reader 128 version 1.00"
  5461. 11 print : print"by craig bruce 09-feb-93 for c=hacking" : print
  5462. 12 :
  5463. 20 cd=peek(186):if cd<8 then cd=8 : rem ** default cbm-dos drive **
  5464. 25 dv=9:dt=0 :  rem ** ms-dos drive, type (0=1571,255=1581)
  5465. 26 if dv=cd then dv=8:dt=0 : rem ** alternate ms-dos drive
  5466. 27 :
  5467. 30 print "initializing..." : print
  5468. 40 bank0 : pk=dec("8000") : pv=pk+30
  5469. 50 if peek(pv+0)=dec("cb") and peek(pv+1)=132 then 60
  5470. 55 print"loading machine language routines..." : bload"lrr.bin",u(cd)
  5471. 60 poke pv+3,dv : poke pv+4,dt : sys pk
  5472. 70 dim t,r,b,i,a$,c,dt$,fl$,il$,x,x$
  5473. 71 cm$="dmftc+-q "+chr$(13)+chr$(145)+chr$(17)+chr$(157)+chr$(29)+chr$(19)
  5474. 72 cm$=cm$+chr$(147)+"/rnx"+chr$(92)
  5475. 75 dl=-1 : cf=-1 : me=0 : ca=0 : ma=0
  5476. 80 dim di$(1,300),cl(128),sz(128),dp(128),cn$(300)
  5477. 90 if dt=255 then dt$="1581" :else dt$="1571"
  5478. 100 fl$=chr$(19)+chr$(17)+chr$(17)+chr$(17)+chr$(17)
  5479. 110 il$=fl$:fori=1to19:il$=il$+chr$(17):next
  5480. 120 goto 500
  5481. 130 :
  5482. 131 rem ** load ms-dos directory **
  5483. 140 print"loading ms-dos directory..." : print
  5484. 150 sys pk : sys pk+3
  5485. 160 dl=0
  5486. 170 rreg bl,dc,bh,s : e=peek(pv+2)
  5487. 180 if (s and 1) then gosub 380 : dl=-1 : return
  5488. 190 print"scanning ms-dos directory..." : print
  5489. 200 db=bl+256*bh
  5490. 205 sys pk+21 : rreg bl,x,bh : ma=bl+bh*256+x*65536
  5491. 210 if dc=0 then 360
  5492. 220 for dp=db to db+32*(dc-1) step 32
  5493. 230 if peek(dp)=0 or peek(dp)=229 then 350
  5494. 240 if peek(dp+11) and 24 then 350
  5495. 250 dl=dl+1
  5496.  
  5497. Line 260 sets the default selection, translation, and filetypes for MS-DOS
  5498. files.  Change to your liking.
  5499.  
  5500. 260 d$=right$(" "+str$(dl),3)+"     asc  seq  " : rem ** default sel/tr/ft **
  5501. 270 a$="" : fori=0to10 : a$=a$+chr$(peek(dp+i)) : next
  5502. 280 a$=left$(a$,8)+"  "+right$(a$,3)
  5503. 290 print dl; a$
  5504. 300 d$=d$+a$+"  "
  5505. 310 cl(dl)=peek(dp+26)+256*peek(dp+27)
  5506. 320 sz=peek(dp+28)+256*peek(dp+29)+65536*peek(dp+30)
  5507. 330 di$(0,dl)=d$+right$("    "+str$(sz),6)
  5508. 335 dp(dl)=dp
  5509. 340 sz(dl)=sz
  5510. 350 next dp
  5511. 360 return
  5512. 370 :
  5513. 371 rem ** report ms-dos disk error **
  5514. 380 print chr$(18);"ms-dos disk error #";mid$(str$(e),2);
  5515. 390 print " ($";mid$(hex$(e),3);"), press key.";chr$(146)
  5516. 400 getkey a$ : return
  5517. 410 :
  5518. 411 rem ** screen heading **
  5519. 420 print chr$(147);chr$(18);
  5520. 421 if me=0 then print"ms-dos";:x=ma:else print"cbmdos";:x=ca
  5521. 422 print chr$(146);"  ms=";mid$(str$(dv),2);":";dt$;
  5522. 430 print"  cbm=";mid$(str$(cd),2);"  free=";mid$(str$(x),2)
  5523. 440 print : return
  5524. 450 :
  5525. 451 rem ** screen footing **
  5526. 460 print il$;"d=dir m=msdev f=cbmdev c=copy q=quit   "
  5527. 470 print     "t=toggle r=remove x=cbmcpy /=menu +-=pg";
  5528. 480 return
  5529. 490 :
  5530. 491 rem ** main routine **
  5531. 500 t=1 : c=0
  5532. 501 r=0
  5533. 510 if me=0 then mf=dl:mc=2 : else mf=cf:mc=1
  5534. 520 gosub 420
  5535. 521 if me<>0 then 542
  5536. 530 print "num  s  trn  typ  filename  ext  length"
  5537. 540 print "---  -  ---  ---  --------  ---  ------"
  5538. 541 goto 550
  5539. 542 print "num  s  trn  filename         t  length"
  5540. 543 print "---  -  ---  ---------------- -  ------"
  5541. 550 gosub 460
  5542. 560 b=t+17 : if b>mf then b=mf
  5543. 570 print fl$;: if t>mf then 590
  5544. 580 for i=t to b : print di$(me,i) : next
  5545. 590 if mf<0 then print chr$(18);"<directory not loaded>";chr$(146)
  5546. 591 if mf=0 then print chr$(18);"<no files>";chr$(146)
  5547. 600 if mf<=0 then 660
  5548. 610 print left$(il$,r+5);chr$(18);
  5549. 620 on c+1 goto 630,640,650
  5550. 630 print spc(4);mid$(di$(me,t+r),5,3) : goto 660
  5551. 640 print spc(7);mid$(di$(me,t+r),8,5) : goto 660
  5552. 650 print spc(12);mid$(di$(me,t+r),13,5) : goto 660
  5553. 660 getkey a$
  5554. 670 i=instr(cm$,a$)
  5555. 680 if mf>0 then print left$(il$,r+5);di$(me,t+r)
  5556. 690 if i=0 then 600
  5557. 700 on i goto 760,1050,1110,950,1150,1000,1020,730,860,860,770,790,810,830,850
  5558. 705 on i-15 goto 500,713,1400,713,1500,713
  5559. 710 stop
  5560. 711 :
  5561. 712 rem ** various menu options **
  5562. 713 me=-(me=0)
  5563. 714 goto500
  5564. 730 print chr$(147);"have an awesome day." : bank15
  5565. 740 end
  5566. 760 if me=1 then gosub 420 : gosub 2500 : goto 500
  5567. 765 gosub 420 : gosub 140 : goto 500
  5568. 770 r=r-1 : if r<0 then r=b-t
  5569. 780 goto 600
  5570. 790 r=r+1 : if t+r>b then r=0
  5571. 800 goto 600
  5572. 810 c=c-1 : if c<0 then c=mc
  5573. 820 goto 600
  5574. 830 c=c+1 : if c>mc then c=0
  5575. 840 goto 600
  5576. 850 r=0 : c=0 : goto 600
  5577. 860 if mf<=0 then 600
  5578. 870 x=t+r : on c+1 gosub 890,910,930
  5579. 880 print left$(il$,r+5);di$(me,x) : goto 600
  5580. 890 if mid$(di$(me,x),6,1)=" " then x$="*" :else x$=" "
  5581. 900 mid$(di$(me,x),6,1)=x$ : return
  5582. 910 if mid$(di$(me,x),9,1)="a" then x$="bin" :else x$="asc"
  5583. 920 mid$(di$(me,x),9,3)=x$ : return
  5584. 930 if mid$(di$(me,x),14,1)="s" then x$="prg" :else x$="seq"
  5585. 940 mid$(di$(me,x),14,3)=x$ : return
  5586. 950 if mf<=0 then 600
  5587. 960 for x=1 to mf
  5588. 970 on c+1 gosub 890,910,930
  5589. 980 next x
  5590. 990 goto 520
  5591. 1000 r=0:if b=mf then t=1 : goto 510
  5592. 1010 t=t+18 : goto 510
  5593. 1020 if mf<=0 then 660
  5594. 1025 r=0:if t=1 then t=mf-(mf-int(mf/18)*18)+1 : if t<=mf then 510
  5595. 1030 t=t-18 : if t<1 then t=1
  5596. 1040 goto 510
  5597. 1050 print il$;chr$(27);"@";
  5598. 1060 input"ms-dos device number (8-30)";dv
  5599. 1061 if cd=dv thenprint"ms-dos and cbm-dos devices must be different!":goto1060
  5600. 1070 x=71 : input"ms-dos device type  (71/81)";x
  5601. 1080 if x=8 or x=81 or x=1581 then dt=255:dt$="1581" :else dt=0:dt$="1571"
  5602. 1090 poke pv+3,dv : poke pv+4,dt : sys pk : dl=-1 : ma=0
  5603. 1100 goto 500
  5604. 1110 print il$;chr$(27);"@";
  5605. 1120 input "cbm-dos device number (0-30)";cd
  5606. 1130 if cd=dv thenprint"ms-dos and cbm-dos devices must be different!":goto1120
  5607. 1140 cf=-1 : ca=0 : goto 500
  5608. 1141 :
  5609. 1142 rem ** copy files **
  5610. 1150 if me=1 then 2000
  5611. 1151 print chr$(147);"copy ms-dos -> cbm-dos":print:print
  5612. 1160 if dl<=0 then fc=0 : goto 1190
  5613. 1170 fc=0 : for f=1 to dl : if mid$(di$(0,f),6,1)="*" then gosub 1200
  5614. 1180 next f
  5615. 1190 print : print"files copied =";fc;" - press key"
  5616. 1191 getkey a$ : goto 520
  5617. 1200 fc=fc+1
  5618. 1210 x$=mid$(di$(0,f),19,8)+"."+mid$(di$(0,f),29,3)
  5619. 1220 cf$="":fori=1tolen(x$):if mid$(x$,i,1)<>" " then cf$=cf$+mid$(x$,i,1)
  5620. 1230 next
  5621. 1231 if right$(cf$,1)="." then cf$=left$(cf$,len(cf$)-1)
  5622. 1232 cf$=cf$+","+mid$(di$(0,f),14,1)
  5623. 1240 print str$(fc);". ";chr$(34);cf$;chr$(34);tab(20);sz(f)"bytes";
  5624. 1245 print tab(35);mid$(di$(0,f),9,3)
  5625. 1250 cl=cl(f) : lb=sz(f) - int(sz(f)/65536)*65536
  5626. 1260 if cd>=8 then dopen#1,(cf$+",w"),u(cd) :else if cd<>0 then open 1,cd,7
  5627. 1265 if cd<8 then 1288
  5628. 1270 if ds<>63 then 1288
  5629. 1275 x$="y" : print "cbm file exists; overwrite (y/n)";
  5630. 1280 close 1 : input x$ : if x$="n" then fc=fc-1 : return
  5631. 1285 scratch(cf$),u(cd)
  5632. 1286 dopen#1,(cf$+",w"),u(cd)
  5633. 1288 if cd<8 then 1320
  5634. 1300 if ds<20 then 1320
  5635. 1310 print chr$(18)+"cbm disk error: "+ds$ : fc=fc-1 : close1 : return
  5636. 1320 poke pv+6,cl/256 : poke pv+5,cl-peek(pv+6)*256
  5637. 1330 poke pv+8,lb/256 : poke pv+7,lb-peek(pv+8)*256
  5638. 1340 tr=0 : if mid$(di$(0,f),9,1)="a" then tr=255
  5639. 1346 x=1 : if cd=0 then x=0
  5640. 1350 sys pk+6,tr,x
  5641. 1355 rreg x,x,x,s : e=peek(pv+2)
  5642. 1356 if (s and 1) then gosub 380 : fc=fc-1
  5643. 1360 if cd<>0 and cd<8 then close1
  5644. 1370 if cd>=8 then dclose#1 : if ds>=20 then 1310
  5645. 1380 return
  5646. 1398 :
  5647. 1399 rem ** remove ms-dos file **
  5648. 1400 print chr$(147);"remove (delete) selected ms-dos files:":print
  5649. 1401 if me<>0 then print"ms-dos menu must be selected!" : goto2030
  5650. 1402 a$="y":input"are you like sure about this (y/n)";a$
  5651. 1403 print:if a$="n" then goto 520
  5652. 1410 if dl<=0 then fc=0 : goto 1440
  5653. 1420 fc=0 : f=1
  5654. 1425 if mid$(di$(0,f),6,1)="*" then gosub 1470 : fc=fc+1 : f=f-1
  5655. 1430 f=f+1 : if f<=dl then 1425
  5656. 1434 print"flushing..."
  5657. 1435 sys pk+12
  5658. 1440 print : print"files removed =";fc;" - press key"
  5659. 1445 sys pk+21 : rreg a,x,y : ma=a+y*256+x*65536
  5660. 1450 getkey a$ : goto 500
  5661. 1470 print"removing ";chr$(34);mid$(di$(0,f),19,13);chr$(34)
  5662. 1490 poke pv+10,dp(f)/256 : poke pv+9,dp(f)-peek(pv+10)*256
  5663. 1492 sys pk+15
  5664. 1494 di$(0,f)=di$(0,dl):sz(f)=sz(dl):dp(f)=dp(dl):cl(f)=cl(dl)
  5665. 1495 dl=dl-1
  5666. 1496 return
  5667. 1498 :
  5668. 1499 rem ** copy cbm files **
  5669. 1500 print chr$(147);"copy cbm-dos to cbm-dos:":print
  5670. 1501 if cf<=0 then print"commodore directory not loaded" : goto 2030
  5671. 1502 x=0 : input"device number to copy to";x : print
  5672. 1503 if x<=0 or x>=64 then print"bad device number!" : goto 2030
  5673. 1504 if x=cd then print"cannot copy to same device" : goto 2030
  5674. 1505 for f=1 to cf : if mid$(di$(1,f),6,1)<>"*" then 1570
  5675. 1506 print di$(1,f) : open1,cd,2,cn$(f)+",r"
  5676. 1507 if x<8 then open 2,x,7 : goto1550
  5677. 1508 cf$=cn$(f)+","+mid$(di$(1,f),31,1)+",w"
  5678. 1509 open2,x,3,cf$
  5679. 1510 if ds<>63 then 1530
  5680. 1511 close2
  5681. 1512 x$="y":input"file exists: overwrite (y/n)";x$ : if x$="n" then 1560
  5682. 1520 scratch(cn$(f)),u(x)
  5683. 1525 open2,x,3,cf$
  5684. 1530 if ds>20 then print chr$(18);"cbm dos error: ";ds$ : goto1560
  5685. 1550 sys pk+24,1,2
  5686. 1560 close1 : close2
  5687. 1570 next f
  5688. 1580 print : print"finished - press a key" : getkey a$ : goto510
  5689. 1998 :
  5690. 1999 rem ** copy cbm-dos to ms-dos **
  5691. 2000 print chr$(147);"copy cbm-dos to ms-dos:" : print : print
  5692. 2010 if dl>=0 then 2035
  5693. 2020 print"ms-dos directory must be loaded first"
  5694. 2030 print : print"press any key" : getkey a$ : goto 510
  5695. 2035 fc=0
  5696. 2036 for f=1 to cf : if mid$(di$(1,f),6,1)<>"*" then 2045
  5697. 2040 fc=fc+1 : c$=cn$(f)
  5698. 2041 printmid$(str$(fc),2);" ";mid$(di$(1,f),14,16);mid$(di$(1,f),34);":";
  5699. 2042 gosub2050 : print left$(m$,8);".";right$(m$,3)
  5700. 2043 tr=0 : if mid$(di$(1,f),9,1)="a" then tr=255
  5701. 2044 gosub2100
  5702. 2045 next
  5703. 2046 print"flushing..." : sys pk+12
  5704. 2047 sys pk+21 : rreg a,x,y : ma=a+y*256+x*65536
  5705. 2048 print: print"files copied =";fc : goto2030
  5706. 2049 :
  5707. 2050 x=instr(c$,".") : if x=0 then m$=c$+"           " : goto2090
  5708. 2055 x=len(c$)+1 : do : x=x-1 : loop until mid$(c$,x,1)="."
  5709. 2060 m$=left$(left$(c$,x-1)+"        ",8)
  5710. 2070 x$=mid$(c$,x+1)+"   "
  5711. 2080 m$=m$+x$
  5712. 2090 m$=left$(m$,11)
  5713. 2091 fori=1to11:x$=chr$(asc(mid$(m$,i,1))and127):if x$="."orx$=" " then x$="_"
  5714. 2092 mid$(m$,i,1)=x$ : next i
  5715. 2093 i=8 : do while i>1 and mid$(m$,i,1)="_" : mid$(m$,i,1)=" " : i=i-1 : loop
  5716. 2094 i=11 : do while i>8 and mid$(m$,i,1)="_" : mid$(m$,i,1)=" " : i=i-1 : loop
  5717. 2098 return
  5718. 2099 :
  5719. 2100 fori=0to0
  5720. 2105 for dp=db to db+32*(dc-1) step 32
  5721. 2110 if peek(dp)=0 or peek(dp)=229 then 2140
  5722. 2120 next dp
  5723. 2130 print"no free ms-dos directory entires" : return
  5724. 2140 next i
  5725. 2160 fori=1tolen(m$):pokedp+i-1,asc(mid$(m$,i,1)) and 127:next
  5726. 2170 fori=11to31:poke dp+i,0:next
  5727. 2180 pokedp+26,255:pokedp+27,15
  5728. 2190 poke pv+10,dp/256:poke pv+9,dp-peek(pv+10)*256
  5729. 2200 open1,cd,2,c$
  5730. 2300 sys pk+9,tr,1 : rreg x,x,x,s
  5731. 2301 close1
  5732. 2305 if s and 1 then e=peek(pv+2) : gosub380 : return
  5733.  
  5734. Line 2310 sets the default MS-DOS selection, translation, and filetype after
  5735. copying to MS-DOS disk, based on the CBM-DOS filetype.  Change to your liking.
  5736.  
  5737. 2310 x$="     asc  seq  ":if tr=0 then x$="     bin  prg  "
  5738. 2320 dl=dl+1 : d$=right$(" "+str$(dl),3)+x$
  5739. 2330 d$=d$+left$(m$,8)+"  "+right$(m$,3)
  5740. 2340 cl(dl)=peek(dp+26)+256*peek(dp+27)
  5741. 2350 sz=peek(dp+28)+256*peek(dp+29)+65536*peek(dp+30)
  5742. 2360 di$(0,dl)=d$+right$("        "+str$(sz),8)
  5743. 2370 dp(dl)=dp
  5744. 2380 sz(dl)=sz
  5745. 2395 return
  5746. 2498 :
  5747. 2499 rem ** load commodore dos directory **
  5748. 2500 print"loading commodore dos directory..." : print
  5749. 2501 if cd<8 then print"cbmdos device must be >= 8!" : goto2030
  5750. 2505 open1,cd,0,"$0":get#1,a$,a$ : cf=-1 : q$=chr$(34)
  5751. 2506 do
  5752. 2507 sys pk+27,1 : b=peek(pv+11)+256*peek(pv+12) : t$=chr$(peek(pv+13))
  5753. 2510 x=peek(pv+14)
  5754. 2520 if x=0 then exit
  5755. 2530 x$="" : for i=pv+15 to pv+15+x-1 : x$=x$+chr$(peek(i)) : next
  5756. 2575 cf=cf+1
  5757. 2590 if cf=0 then print"disk="q$x$q$ : print : goto2650
  5758. 2600 cn$(cf)=x$
  5759. 2610 a$=left$(x$+"                 ",17)+t$+right$("       "+str$(b*254),8)
  5760.  
  5761. Lines 2620 and 2625 set the default CBM-DOS selection and translation modes
  5762. based on the filetype.  Change to your liking.
  5763.  
  5764. 2620 di$(1,cf)=right$("  "+str$(cf),3)+"     asc  "+a$
  5765. 2625 if t$<>"s" then mid$(di$(1,cf),9,3)="bin"
  5766. 2630 print di$(1,cf)
  5767. 2650 loop
  5768. 2670 ca=b*256 : close1 : return
  5769.  
  5770. 6. UUENCODED FILES
  5771.  
  5772. Here are the binary executables in uuencoded form.  The CRC32s of the two
  5773. files are as follows:
  5774.  
  5775. crc32 = 3896271974 for "lrr-128"
  5776. crc32 = 2918283051 for "lrr.bin"
  5777.  
  5778. The "lrr.128" file is the main BASIC program and the "lrr.bin" file contains
  5779. the machine lanugage disk-accessing routines.
  5780.  
  5781. begin 640 lrr-128
  5782. M`1PS'`H`F2#'*#$T-RD[(DQ)5%1,12!2140@4D5!1$52(#$R."!615)324].
  5783. M(#$N,#`B`&D<"P"9(#H@F2)"62!#4D%)1R!"4E5#12`P.2U&14(M.3,@1D]2
  5784. M($,]2$%#2TE.1R(@.B"9`&\<#``Z`*L<%`!#1++"*#$X-BDZBR!#1+,X(*<@
  5785. M0T2R."`Z((\@*BH@1$5&055,5"!#0DTM1$]3($12259%("HJ`.8<&0!$5K(Y
  5786. M.D14LC`@.B`@CR`J*B!-4RU$3U,@1%))5D4L(%194$4@*#`],34W,2PR-34]
  5787. M,34X,2D`'!T:`(L@1%:R0T0@IR!$5K(X.D14LC`@.B"/("HJ($%,5$523D%4
  5788. M12!-4RU$3U,@1%))5D4`(AT;`#H`/AT>`)D@(DE.251)04Q)6DE.1RXN+B(@
  5789. M.B"9`&`=*`#^`C`@.B!02[+1*"(X,#`P(BD@.B!05K)02ZHS,`")'3(`BR#"
  5790. M*%!6JC`ILM$H(D-"(BD@KR#"*%!6JC$ILC$S,B"G(#8P`,D=-P"9(DQ/041)
  5791. M3D<@34%#2$E.12!,04Y'54%'12!23U5424Y%4RXN+B(@.B#^$2),4E(N0DE.
  5792. M(BQ5*$-$*0#J'3P`ER!05JHS+$16(#H@ER!05JHT+$14(#H@GB!02P`.'D8`
  5793. MAB!4+%(L0BQ)+$$D+$,L1%0D+$9,)"Q)3"0L6"Q8)`!('D<`0TTDLB)$3494
  5794. M0RLM42`BJL<H,3,IJL<H,30U*:K'*#$W*:K'*#$U-RFJQR@R.2FJQR@Q.2D`
  5795. M:!Y(`$--)+)#322JQR@Q-#<IJB(O4DY8(JK'*#DR*0"/'DL`1$RRJS$@.B!#
  5796. M1K*K,2`Z($U%LC`@.B!#0;(P(#H@34&R,`#!'E``AB!$220H,2PS,#`I+$-,
  5797. M*#$R."DL4UHH,3(X*2Q$4"@Q,C@I+$-.)"@S,#`I`.D>6@"+($14LC(U-2"G
  5798. M($14)+(B,34X,2(@.M4@1%0DLB(Q-3<Q(@`/'V0`1DPDLL<H,3DIJL<H,3<I
  5799. MJL<H,3<IJL<H,3<IJL<H,3<I`#,?;@!)3"2R1DPD.H%)LC&D,3DZ24PDLDE,
  5800. M)*K'*#$W*3J"`#T?>`")(#4P,`!#'X(`.@!E'X,`CR`J*B!,3T%$($U3+41/
  5801. M4R!$25)%0U1/4ED@*BH`C!^,`)DB3$]!1$E.1R!-4RU$3U,@1$E214-43U)9
  5802. M+BXN(B`Z()D`GA^6`)X@4$L@.B">(%!+JC,`IQ^@`$1,LC``Q1^J`/X)($),
  5803. M+$1#+$)(+%,@.B!%LL(H4%:J,BD`YQ^T`(L@*%,@KR`Q*2"G((T@,S@P(#H@
  5804. M1$RRJS$@.B".``\@O@"9(E-#04Y.24Y'($U3+41/4R!$25)%0U1/4EDN+BXB
  5805. M(#H@F0`@(,@`1$*R0DRJ,C4VK$)(`%`@S0">(%!+JC(Q(#H@_@D@0DPL6"Q"
  5806. M2"`Z($U!LD),JD)(K#(U-JI8K#8U-3,V`&$@T@"+($1#LC`@IR`S-C``@2#<
  5807. M`($@1%"R1$(@I"!$0JHS,JPH1$.K,2D@J2`S,@"A(.8`BR#"*$10*;(P(+`@
  5808. MPBA$4"FR,C(Y(*<@,S4P`+L@\`"+(,(H1%"J,3$I(*\@,C0@IR`S-3``QR#Z
  5809. M`$1,LD1,JC$`"R$$`40DLLDH(B`BJL0H1$PI+#,IJB(@("`@($%30R`@4T51
  5810. M("`B(#H@CR`J*B!$149!54Q4(%-%3"]44B]&5"`J*@`V(0X!022R(B(@.B"!
  5811. M2;(PI#$P(#H@022R022JQRC"*$10JDDI*2`Z(((`4B$8`4$DLL@H020L."FJ
  5812. M(B`@(JK)*$$D+#,I`%\A(@&9($1,.R!!)`!Q(2P!1"2R1"2J022J(B`@(@"2
  5813. M(38!0TPH1$PILL(H1%"J,C8IJC(U-JS"*$10JC(W*0"^(4`!4UJRPBA$4*HR
  5814. M."FJ,C4VK,(H1%"J,CDIJC8U-3,VK,(H1%"J,S`I`.$A2@%$220H,"Q$3"FR
  5815. M1"2JR2@B("`@("*JQ"A36BDL-BD`[R%/`410*$1,*;)$4`#](50!4UHH1$PI
  5816. MLE-:``8B7@&"($10``PB:`&.`!(B<@$Z`#<B<P&/("HJ(%)%4$]25"!-4RU$
  5817. M3U,@1$E32R!%4E)/4B`J*@!D(GP!F2#'*#$X*3LB35,M1$]3($1)4TL@15)2
  5818. M3U(@(R([RBC$*$4I+#(I.P"1(H8!F2`B("@D(CO**-(H12DL,RD[(BDL(%!2
  5819. M15-3($M%62XB.\<H,30V*0"?(I`!H?D@020@.B".`*4BF@$Z`,`BFP&/("HJ
  5820. M(%-#4D5%3B!(14%$24Y'("HJ`-0BI`&9(,<H,30W*3O'*#$X*3L``R.E`8L@
  5821. M346R,""G()DB35,M1$]3(CLZ6+)-03K5()DB0T)-1$]3(CLZ6+)#00`L(Z8!
  5822. MF2#'*#$T-BD[(B`@35,](CO**,0H1%8I+#(I.R(Z(CM$5"0[`%DCK@&9(B`@
  5823. M0T)-/2([RBC$*$-$*2PR*3LB("!&4D5%/2([RBC$*%@I+#(I`&,CN`&9(#H@
  5824. MC@!I(\(!.@"$(\,!CR`J*B!30U)%14X@1D]/5$E.1R`J*@"X(\P!F2!)3"0[
  5825. M(D0]1$E2($T]35-$158@1CU#0DU$158@0SU#3U!9(%$]455)5"`@("(`[2/6
  5826. M`9D@("`@(")4/51/1T=,12!2/5)%34]612!8/4-"34-062`O/4U%3E4@*RT]
  5827. M4$<B.P#S(^`!C@#Y(^H!.@`2).L!CR`J*B!-04E.(%)/551)3D4@*BH`("3T
  5828. M`52R,2`Z($.R,``H)/4!4K(P`$\D_@&+($U%LC`@IR!-1K)$3#I-0[(R(#H@
  5829. MU2!-1K)#1CI-0[(Q`%DD"`*-(#0R,`!K)`D"BR!-1;.Q,""G(#4T,@";)!("
  5830. MF2`B3E5-("!3("!44DX@(%194"`@1DE,14Y!344@($585"`@3$5.1U1((@#+
  5831. M)!P"F2`B+2TM("`M("`M+2T@("TM+2`@+2TM+2TM+2T@("TM+2`@+2TM+2TM
  5832. M(@#5)!T"B2`U-3``!24>`ID@(DY532`@4R`@5%).("!&24Q%3D%-12`@("`@
  5833. M("`@(%0@($Q%3D=42"(`-24?`ID@(BTM+2`@+2`@+2TM("`M+2TM+2TM+2TM
  5834. M+2TM+2TM("T@("TM+2TM+2(`/R4F`HT@-#8P`%HE,`)"LE2J,3<@.B"+($*Q
  5835. M348@IR!"LDU&`',E.@*9($9,)#LZ((L@5+%-1B"G(#4Y,`"3)40"@2!)LE0@
  5836. MI"!"(#H@F2!$220H344L22D@.B""`,@E3@*+($U&LS`@IR"9(,<H,3@I.R(\
  5837. M1$E214-43U)9($Y/5"!,3T%$140^(CO'*#$T-BD`\25/`HL@34:R,""G()D@
  5838. MQR@Q."D[(CQ.3R!&24Q%4SXB.\<H,30V*0`#)E@"BR!-1K.R,""G(#8V,``;
  5839. M)F("F2#(*$E,)"Q2JC4I.\<H,3@I.P`S)FP"D2!#JC$@B2`V,S`L-C0P+#8U
  5840. M,`!8)G8"F2"F-"D[RBA$220H344L5*I2*2PU+#,I(#H@B2`V-C``?2:``ID@
  5841. MIC<I.\HH1$DD*$U%+%2J4BDL."PU*2`Z((D@-C8P`*0FB@*9(*8Q,BD[RBA$
  5842. M220H344L5*I2*2PQ,RPU*2`Z((D@-C8P`*XFE`*A^2!!)`"^)IX"2;+4*$--
  5843. M)"Q!)"D`Y":H`HL@34:Q,""G()D@R"A)3"0L4JHU*3M$220H344L5*I2*0#T
  5844. M)K("BR!)LC`@IR`V,#``/R>\`I$@22")(#<V,"PQ,#4P+#$Q,3`L.34P+#$Q
  5845. M-3`L,3`P,"PQ,#(P+#<S,"PX-C`L.#8P+#<W,"PW.3`L.#$P+#@S,"PX-3``
  5846. M9B?!`I$@2:LQ-2")(#4P,"PW,3,L,30P,"PW,3,L,34P,"PW,3,`;"?&`I``
  5847. M<B?'`CH`DR?(`H\@*BH@5D%224]54R!-14Y5($]05$E/3E,@*BH`HB?)`DU%
  5848. MLJLH346R,"D`JR?*`HDU,#``UB?:`ID@QR@Q-#<I.R)(059%($%.($%715-/
  5849. M344@1$%9+B(@.B#^`C$U`-PGY`*````H^`*+($U%LC$@IR"-(#0R,"`Z((T@
  5850. M,C4P,"`Z((D@-3`P`!HH_0*-(#0R,"`Z((T@,30P(#H@B2`U,#``-"@"`U*R
  5851. M4JLQ(#H@BR!2LS`@IR!2LD*K5``^*`P#B2`V,#``6"@6`U*R4JHQ(#H@BR!4
  5852. MJE*Q0B"G(%*R,`!B*"`#B2`V,#``>R@J`T.R0ZLQ(#H@BR!#LS`@IR!#LDU#
  5853. M`(4H-`.)(#8P,`">*#X#0[)#JC$@.B"+($.Q34,@IR!#LC``J"A(`XD@-C`P
  5854. M`+XH4@-2LC`@.B!#LC`@.B")(#8P,`#0*%P#BR!-1K.R,""G(#8P,`#P*&8#
  5855. M6+)4JE(@.B"1($.J,2"-(#@Y,"PY,3`L.3,P`!,I<`.9(,@H24PD+%*J-2D[
  5856. M1$DD*$U%+%@I(#H@B2`V,#``02EZ`XL@RBA$220H344L6"DL-BPQ*;(B("(@
  5857. MIR!8)+(B*B(@.M4@6"2R(B`B`%TIA`/**$1))"A-12Q8*2PV+#$ILE@D(#H@
  5858. MC@"/*8X#BR#**$1))"A-12Q8*2PY+#$ILB)!(B"G(%@DLB)"24XB(#K5(%@D
  5859. MLB)!4T,B`*LIF`/**$1))"A-12Q8*2PY+#,ILE@D(#H@C@#>*:(#BR#**$1)
  5860. M)"A-12Q8*2PQ-"PQ*;(B4R(@IR!8)+(B4%)'(B`ZU2!8)+(B4T51(@#[*:P#
  5861. MRBA$220H344L6"DL,30L,RFR6"0@.B".``TJM@.+($U&L[(P(*<@-C`P`!PJ
  5862. MP`.!(%BR,2"D($U&`#0JR@.1($.J,2"-(#@Y,"PY,3`L.3,P`#PJU`."(%@`
  5863. M1BK>`XD@-3(P`&,JZ`-2LC`ZBR!"LDU&(*<@5+(Q(#H@B2`U,3``=BKR`U2R
  5864. M5*HQ."`Z((D@-3$P`(@J_`.+($U&L[(P(*<@-C8P`,`J`012LC`ZBR!4LC$@
  5865. MIR!4LDU&JRA-1JNU*$U&K3$X*:PQ."FJ,2`Z((L@5+.R348@IR`U,3``V2H&
  5866. M!%2R5*LQ."`Z((L@5+,Q(*<@5+(Q`.,J$`2)(#4Q,`#X*AH$F2!)3"0[QR@R
  5867. M-RD[(D`B.P`>*R0$A2)-4RU$3U,@1$5624-%($Y534)%4B`H."TS,"DB.T16
  5868. M`&(K)02+($-$LD16(*>9(DU3+41/4R!!3D0@0T)-+41/4R!$159)0T53($U5
  5869. M4U0@0D4@1$E&1D5214Y4(2(ZB3$P-C``CBLN!%BR-S$@.B"%(DU3+41/4R!$
  5870. M159)0T4@5%E012`@*#<Q+S@Q*2([6`#/*S@$BR!8LC@@L"!8LC@Q(+`@6+(Q
  5871. M-3@Q(*<@1%2R,C4U.D14)+(B,34X,2(@.M4@1%2R,#I$5"2R(C$U-S$B`/\K
  5872. M0@27(%!6JC,L1%8@.B"7(%!6JC0L1%0@.B">(%!+(#H@1$RRJS$@.B!-0;(P
  5873. M``DL3`2)(#4P,``>+%8$F2!)3"0[QR@R-RD[(D`B.P!&+&`$A2`B0T)-+41/
  5874. M4R!$159)0T4@3E5-0D52("@P+3,P*2([0T0`BBQJ!(L@0T2R1%8@IYDB35,M
  5875. M1$]3($%.1"!#0DTM1$]3($1%5DE#15,@35535"!"12!$249&15)%3E0A(CJ)
  5876. M,3$R,`"C+'0$0T:RJS$@.B!#0;(P(#H@B2`U,#``J2QU!#H`P"QV!(\@*BH@
  5877. M0T]062!&24Q%4R`J*@#2+'X$BR!-1;(Q(*<@,C`P,`#\+'\$F2#'*#$T-RD[
  5878. M(D-/4%D@35,M1$]3("T^($-"32U$3U,B.IDZF0`8+8@$BR!$3+.R,""G($9#
  5879. MLC`@.B")(#$Q.3``3RV2!$9#LC`@.B"!($:R,2"D($1,(#H@BR#**$1))"@P
  5880. M+$8I+#8L,2FR(BHB(*<@C2`Q,C`P`%<MG`2"($8`@RVF!)D@.B"9(D9)3$53
  5881. M($-/4$E%1"`](CM&0SLB("T@4%)%4U,@2T59(@"5+:<$H?D@020@.B")(#4R
  5882. M,`"A+;`$1D.R1D.J,0#.+;H$6"2RRBA$220H,"Q&*2PQ.2PX*:HB+B*JRBA$
  5883. M220H,"Q&*2PR.2PS*0`)+L0$0T8DLB(B.H%)LC&DPRA8)"DZBR#**%@D+$DL
  5884. M,2FSL2(@(B"G($-&)+)#1B2JRBA8)"Q)+#$I``\NS@2"`#@NSP2+(,DH0T8D
  5885. M+#$ILB(N(B"G($-&)++(*$-&)"S#*$-&)"FK,2D`62[0!$-&)+)#1B2J(BPB
  5886. MJLHH1$DD*#`L1BDL,30L,2D`C2[8!)D@Q"A&0RD[(BX@(CO'*#,T*3M#1B0[
  5887. MQR@S-"D[HS(P*3M36BA&*2)"651%4R([`*@NW029(*,S-2D[RBA$220H,"Q&
  5888. M*2PY+#,I`-<NX@1#3+)#3"A&*2`Z($Q"LE-:*$8I(*L@M2A36BA&*:TV-34S
  5889. M-BFL-C4U,S8`$2_L!(L@0T2QLC@@IR#^#2,Q+"A#1B2J(BQ7(BDL52A#1"D@
  5890. M.M4@BR!#1+.Q,""G()\@,2Q#1"PW`",O\02+($-$LS@@IR`Q,C@X`#<O]@2+
  5891. M($13L[$V,R"G(#$R.#@`:B_[!%@DLB)9(B`Z()D@(D-"32!&24Q%($5825-4
  5892. M4SL@3U9%4E=2251%("A9+TXI(CL`DB\`!:`@,2`Z((4@6"0@.B"+(%@DLB).
  5893. M(B"G($9#LD9#JS$@.B".`*,O!07R*$-&)"DL52A#1"D`O2\&!?X-(S$L*$-&
  5894. M)*HB+%<B*2Q5*$-$*0#/+P@%BR!#1+,X(*<@,3,R,`#B+Q0%BR!$4[,R,""G
  5895. M(#$S,C``&#`>!9D@QR@Q."FJ(D-"32!$25-+($524D]2.B`BJD13)"`Z($9#
  5896. MLD9#JS$@.B"@,2`Z((X`0C`H!9<@4%:J-BQ#3*TR-38@.B"7(%!6JC4L0TRK
  5897. MPBA05JHV*:PR-38`;#`R!9<@4%:J."Q,0JTR-38@.B"7(%!6JC<L3$*KPBA0
  5898. M5JHX*:PR-38`EC`\!512LC`@.B"+(,HH1$DD*#`L1BDL.2PQ*;(B02(@IR!4
  5899. M4K(R-34`K3!"!5BR,2`Z((L@0T2R,""G(%BR,`"],$8%GB!02ZHV+%12+%@`
  5900. MV#!+!?X)(%@L6"Q8+%,@.B!%LL(H4%:J,BD`^#!,!8L@*%,@KR`Q*2"G((T@
  5901. M,S@P(#H@1D.R1D.K,0`0,5`%BR!#1+.Q,""O($-$LS@@IR"@,0`U,5H%BR!#
  5902. M1+&R.""G(/X/(S$@.B"+($13L;(R,""G(#$S,3``.S%D!8X`03%V!3H`8#%W
  5903. M!8\@*BH@4D5-3U9%($U3+41/4R!&24Q%("HJ`)@Q>`69(,<H,30W*3LB4D5-
  5904. M3U9%("A$14Q%5$4I(%-%3$5#5$5$($U3+41/4R!&24Q%4SHB.ID`SS%Y!8L@
  5905. M346SL3`@IR"9(DU3+41/4R!-14Y5($U54U0@0D4@4T5,14-4140A(B`Z((DR
  5906. M,#,P``,R>@5!)+(B62(ZA2)!4D4@64]5($Q)2T4@4U5212!!0D]55"!42$E3
  5907. M("A9+TXI(CM!)``:,GL%F3J+($$DLB).(B"G((D@-3(P`#8R@@6+($1,L[(P
  5908. M(*<@1D.R,"`Z((D@,30T,`!%,HP%1D.R,"`Z($:R,0!Z,I$%BR#**$1))"@P
  5909. M+$8I+#8L,2FR(BHB(*<@C2`Q-#<P(#H@1D.R1D.J,2`Z($:R1JLQ`)4RE@5&
  5910. MLD:J,2`Z((L@1K.R1$P@IR`Q-#(U`*@RF@69(D9,55-(24Y'+BXN(@"T,IL%
  5911. MGB!02ZHQ,@#A,J`%F2`Z()DB1DE,15,@4D5-3U9%1"`](CM&0SLB("T@4%)%
  5912. M4U,@2T59(@`-,Z4%GB!02ZHR,2`Z(/X)($$L6"Q9(#H@34&R0:I9K#(U-JI8
  5913. MK#8U-3,V`!\SJ@6A^2!!)"`Z((D@-3`P`$XSO@69(E)%34]624Y'("([QR@S
  5914. M-"D[RBA$220H,"Q&*2PQ.2PQ,RD[QR@S-"D`@#/2!9<@4%:J,3`L1%`H1BFM
  5915. M,C4V(#H@ER!05JHY+$10*$8IJ\(H4%:J,3`IK#(U-@",,]0%GB!02ZHQ-0#*
  5916. M,]8%1$DD*#`L1BFR1$DD*#`L1$PI.E-:*$8ILE-:*$1,*3I$4"A&*;)$4"A$
  5917. M3"DZ0TPH1BFR0TPH1$PI`-8SUP5$3+)$3*LQ`-PSV`6.`.(SV@4Z`/TSVP6/
  5918. M("HJ($-/4%D@0T)-($9)3$53("HJ`"<TW`69(,<H,30W*3LB0T]062!#0DTM
  5919. M1$]3(%1/($-"32U$3U,Z(CJ9`&`TW06+($-&L[(P(*<@F2)#3TU-3T1/4D4@
  5920. M1$E214-43U)9($Y/5"!,3T%$140B(#H@B2`R,#,P`(PTW@58LC`@.B"%(D1%
  5921. M5DE#12!.54U"15(@5$\@0T]062!43R([6"`Z()D`P#3?!8L@6+.R,""P(%BQ
  5922. MLC8T(*<@F2)"040@1$5624-%($Y534)%4B$B(#H@B2`R,#,P`/0TX`6+(%BR
  5923. M0T0@IR"9(D-!3DY/5"!#3U!9(%1/(%-!344@1$5624-%(B`Z((D@,C`S,``C
  5924. M->$%@2!&LC$@I"!#1B`Z((L@RBA$220H,2Q&*2PV+#$IL[$B*B(@IR`Q-3<P
  5925. M`$@UX@69($1))"@Q+$8I(#H@GS$L0T0L,BQ#3B0H1BFJ(BQ2(@!D->,%BR!8
  5926. MLS@@IR"?(#(L6"PW(#H@B3$U-3``C37D!4-&)+)#3B0H1BFJ(BPBJLHH1$DD
  5927. M*#$L1BDL,S$L,2FJ(BQ7(@"<->4%GS(L6"PS+$-&)`"P->8%BR!$4[.Q-C,@
  5928. MIR`Q-3,P`+<UYP6@,@#W->@%6"2R(EDB.H4B1DE,12!%6$E35%,Z($]615)7
  5929. M4DE412`H62].*2([6"0@.B"+(%@DLB).(B"G(#$U-C``"C;P!?(H0TXD*$8I
  5930. M*2Q5*%@I`!DV]06?,BQ8+#,L0T8D`$TV^@6+($13L3(P(*<@F2#'*#$X*3LB
  5931. M0T)-($1/4R!%4E)/4CH@(CM$4R0@.B"),34V,`!=-@X&GB!02ZHR-"PQ+#(`
  5932. M:388!J`Q(#H@H#(`<38B!H(@1@"B-BP&F2`Z()DB1DE.25-(140@+2!04D53
  5933. M4R!!($M%62(@.B"A^2!!)"`Z((DU,3``J#;.!SH`RS;/!X\@*BH@0T]062!#
  5934. M0DTM1$]3(%1/($U3+41/4R`J*@#Z-M`'F2#'*#$T-RD[(D-/4%D@0T)-+41/
  5935. M4R!43R!-4RU$3U,Z(B`Z()D@.B"9``TWV@>+($1,L;(P(*<@,C`S-0`Z-^0'
  5936. MF2)-4RU$3U,@1$E214-43U)9($U54U0@0D4@3$]!1$5$($9)4E-4(@!C-^X'
  5937. MF2`Z()DB4%)%4U,@04Y9($M%62(@.B"A^2!!)"`Z((D@-3$P`&PW\P=&0[(P
  5938. M`)LW]`>!($:R,2"D($-&(#H@BR#**$1))"@Q+$8I+#8L,2FSL2(J(B"G(#(P
  5939. M-#4`LS?X!T9#LD9#JC$@.B!#)+)#3B0H1BD`[3?Y!YG**,0H1D,I+#(I.R(@
  5940. M(CO**$1))"@Q+$8I+#$T+#$V*3O**$1))"@Q+$8I+#,T*3LB.B([``\X^@>-
  5941. M,C`U,"`Z()D@R"A-)"PX*3LB+B([R2A-)"PS*0`Y./L'5%*R,"`Z((L@RBA$
  5942. M220H,2Q&*2PY+#$ILB)!(B"G(%12LC(U-0!#./P'C3(Q,#``23C]!X(`9CC^
  5943. M!YDB1DQ54TA)3D<N+BXB(#H@GB!02ZHQ,@"2./\'GB!02ZHR,2`Z(/X)($$L
  5944. M6"Q9(#H@34&R0:I9K#(U-JI8K#8U-3,V`+8X``B9.B"9(D9)3$53($-/4$E%
  5945. M1"`](CM&0R`Z((DR,#,P`+PX`0@Z`/(X`@A8LM0H0R0L(BXB*2`Z((L@6+(P
  5946. M(*<@322R0R2J(B`@("`@("`@("`@(B`Z((DR,#DP`"`Y!PA8LL,H0R0IJC$@
  5947. M.B#K(#H@6+)8JS$@.B#L(/P@RBA#)"Q8+#$ILB(N(@!!.0P(322RR"C(*$,D
  5948. M+%BK,2FJ(B`@("`@("`@(BPX*0!8.18(6"2RRBA#)"Q8JC$IJB(@("`B`&4Y
  5949. M(`A-)+)-)*I8)`!U.2H(322RR"A-)"PQ,2D`L3DK"(%)LC&D,3$Z6"2RQRC&
  5950. M*,HH320L22PQ*2FO,3(W*3J+(%@DLB(N(K!8)+(B("(@IR!8)+(B7R(`R#DL
  5951. M",HH320L22PQ*;)8)"`Z(((@20`&.BT(2;(X(#H@ZR#]($FQ,2"O(,HH320L
  5952. M22PQ*;(B7R(@.B#**$TD+$DL,2FR(B`B(#H@2;))JS$@.B#L`$4Z+@A)LC$Q
  5953. M(#H@ZR#]($FQ.""O(,HH320L22PQ*;(B7R(@.B#**$TD+$DL,2FR(B`B(#H@
  5954. M2;))JS$@.B#L`$LZ,@B.`%$Z,P@Z`%PZ-`B!2;(PI#``?#HY"($@1%"R1$(@
  5955. MI"!$0JHS,JPH1$.K,2D@J2`S,@"=.CX(BR#"*$10*;(P(+`@PBA$4"FR,C(Y
  5956. M(*<@,C$T,`"F.D@(@B!$4`#2.E((F2).3R!&4D5%($U3+41/4R!$25)%0U1/
  5957. M4ED@14Y425)%4R(@.B".`-HZ7`B"($D`!CMP"(%)LC&DPRA-)"DZET10JDFK
  5958. M,2S&*,HH320L22PQ*2D@KR`Q,C<Z@@`>.WH(@4FR,3&D,S$ZER!$4*I)+#`Z
  5959. M@@`W.X0(ET10JC(V+#(U-3J71%"J,C<L,34`83N.")<@4%:J,3`L1%"M,C4V
  5960. M.I<@4%:J.2Q$4*O"*%!6JC$P*:PR-38`<#N8")\Q+$-$+#(L0R0`C3O\")X@
  5961. M4$NJ.2Q44BPQ(#H@_@D@6"Q8+%@L4P"4._T(H#$`MSL!"8L@4R"O(#$@IR!%
  5962. MLL(H4%:J,BD@.B"-,S@P(#H@C@#N.P8)6"2R(B`@("`@05-#("!315$@("(Z
  5963. MBR!44K(P(*<@6"2R(B`@("`@0DE.("!04D<@("(`$3P0"41,LD1,JC$@.B!$
  5964. M)++)*"(@(JK$*$1,*2PS*:I8)``P/!H)1"2R1"2JR"A-)"PX*:HB("`BJLDH
  5965. M320L,RD`43PD"4-,*$1,*;+"*$10JC(V*:HR-3:LPBA$4*HR-RD`?3PN"5-:
  5966. MLL(H1%"J,C@IJC(U-JS"*$10JC(Y*:HV-34S-JS"*$10JC,P*0"D/#@)1$DD
  5967. M*#`L1$PILD0DJLDH(B`@("`@("`@(JK$*%-:*2PX*0"R/$()1%`H1$PILD10
  5968. M`,`\3`E36BA$3"FR4UH`QCQ;"8X`S#S""3H`]3S#"8\@*BH@3$]!1"!#3TU-
  5969. M3T1/4D4@1$]3($1)4D5#5$]262`J*@`C/<0)F2),3T%$24Y'($-/34U/1$]2
  5970. M12!$3U,@1$E214-43U)9+BXN(B`Z()D`5SW%"8L@0T2S.""G()DB0T)-1$]3
  5971. M($1%5DE#12!-55-4($)%(#X](#@A(B`Z((DR,#,P`(4]R0F?,2Q#1"PP+"(D
  5972. M,"(ZH2,Q+$$D+$$D(#H@0T:RJS$@.B!1)++'*#,T*0"+/<H)ZP#$/<L)GB!0
  5973. M2ZHR-RPQ(#H@0K+"*%!6JC$Q*:HR-3:LPBA05JHQ,BD@.B!4)++'*,(H4%:J
  5974. M,3,I*0#3/<X)6++"*%!6JC$T*0#A/=@)BR!8LC`@IR#M`!<^X@E8)+(B(B`Z
  5975. M(($@2;)05JHQ-2"D(%!6JC$UJEBK,2`Z(%@DLE@DJL<HPBA)*2D@.B""`",^
  5976. M#PI#1K)#1JHQ`$L^'@J+($-&LC`@IR"9(D1)4TL](E$D6"11)"`Z()D@.B")
  5977. M,C8U,`!:/B@*0TXD*$-&*;)8)`"9/C(*022RR"A8)*HB("`@("`@("`@("`@
  5978. M("`@("`B+#$W*:I4)*K)*"(@("`@("`@(JK$*$*L,C4T*2PX*0#'/CP*1$DD
  5979. M*#$L0T8ILLDH(B`@(JK$*$-&*2PS*:HB("`@("!!4T,@("*J020`[CY!"HL@
  5980. M5"2SL2)3(B"G(,HH1$DD*#$L0T8I+#DL,RFR(D))3B(`_CY&"ID@1$DD*#$L
  5981. B0T8I``0_6@KL`!H_;@I#0;)"K#(U-B`Z(*`Q(#H@C@``````
  5982. `
  5983. end
  5984. begin 640 lrr.bin
  5985. M`(!,#(),7(-,A89,\X=,,(5,Z(1,/8!,HX5,2XA,@(C+A```````````````
  5986. M````````````````````````````````````````````````````````````
  5987. M````2*D`A9"M(8`@L?^I;R"3_ZE5(*C_))`P#ZDP(*C_:""H_R20,`(88*D%
  5988. MC2"`.&"M`-U)$(T`W6"I""P-W/#[8""3@*X,W""*@(I@J1H@6X"0`6`@KO\D
  5989. MD##.&"!'_RP-W""*@"";@(T@@"D/R0*P):``()N`F56`R,`&D/48J0@@6X"0
  5990. M`6"I`2PB@#`"J00@J/\@KO]@2(HI`0H*"@HL(H`0`DD0(%N`:)`!8""H_ZD!
  5991. M(*C_J0D@J/\@KO]X&"!'_RP-W""*@*GVH(J%`H0#J0"%!"PB@#`#($:!(%Z!
  5992. ML`GF!*4$R0F0ZQA88*GVA0*F!!BIBGU5@84#8``($`8.!`P""B";@(T@@"D/
  5993. MR0*0`6"B`J``J0@L#=SP^ZT`W4D0C0#=K0S<D0+(T.GF`\K0Y&!(A`2**0$*
  5994. M"@H*"0(L(H`0`DD0(%N`:)`!8""H_Z4$(*C_J0$@J/\@KO]XJ4"%!3@@1_]X
  5995. M+`W<H@*@`*T`W<T`W=#X104I0/#RL0*-#-RE!4E`A06I""P-W/#[R-#=Y@/*
  5996. MT-@8($?_+`W<((J`().`K@S<((J`BHT@@"D/R0)88*D.C0#_J?^-2H"-2X"B
  5997. M!YU-@,H0^HU,@!A@S4J`T!#L2X#0"XB8"AAIBJBI]AA@C4J`CDN`A`4@\8"0
  5998. M&:T@@"D/R0OP`CA@(*:`K4J`KDN`I`60VV"M2H"N2X"D!4PF@J+_Z#CI$K#Z
  5999. MB!#W&&D2R*B*8"!L@J(`P`F0"$B8Z0FH:*(!R&"&"R!^@H4(A@F$"J4(I@FD
  6000. M"B`F@I`!8(4,A`VB`J``L0R1!LBQ#)$&R-#TY@WF!\K0[>8*I0K)"I`2J0&%
  6001. M"N8)I0G)`I`&J0"%">8(Q@O0NQA@..D"L`&(KCZ`X`'P!PJ$!R8'I`<8;42`
  6002. MD`'(8"#C@J+VA@:BG(8'KCZ`3)&"('Z"S4J`T`_L2X#0"DBI_XU*@(U+@&@@
  6003. MC(%@```@XX*B]H8"HIR&`XTK@XPL@R`0@Y`!8*T^@,D"L`%@K2N#K"R#&&D!
  6004. MD`'((!"#8*D.C0#_J0"@`"!^@B`F@I`!8(4"A`.@#;$"C3Z`R0.0!ZD\C2"`
  6005. M.&"@$+$"R0+0\:`6L0*-/X#)!+#FH!&Q`HU!@,F!L-M*2DI*C4"`H!.Q`HU"
  6006. M@,BQ`HU#@*`8L0+)"="_H!JQ`LD"T+>@#K$"R0'0KZT_@`H8:0&-18`8;4"`
  6007. MC42`K4*`K$.`..U$@+`!B(U&@(Q'@*T^@,D"T`9.1X!N1H`8K4:`:0*-2("M
  6008. M1X!I`(U)@*GVH*"%!H0'J0&@`*X_@""1@I`!8*GVH*:%!H0'K46`H`"N0(`@
  6009. MD8*0`6"I]J"FKD&`&&"%!80#1@-JA02F`PHF`QAE!(4"BF4#A0,8I0)I]H4"
  6010. MI0-IH(4#H`*Q`ID&`(@0^&`@1(2E!2D!T`BE!RD/J*4&8*4'H@1&"&K*T/JD
  6011. M"&`@1(2E"BD/A0JE!2D!T`^E"84&I0<I\`4*A0=,Q82B!`8))@K*T/FE"H4(
  6012. MI0<I#P4)A0>@`KD&`)$"B!#XC$R`8#BM)X#I]JTH@.FF2BD'JJG_G4V`8*`.
  6013. MC`#_K2>`K"B`A0*$`ZGEH`"1`J`:L0*%#LBQ`H4/I0_)!9`#3-.$J*4.('2$
  6014. M2)A(J0"%"84*I0ZD#R"2A&B%#VB%#DP(A:D.C0#_K4R`\"ZI`(U,@*D"A6&I
  6015. M`84.K3^`A6"I]J"@A0*$`Z4.H``@$(.0`6#F#L9@T/#&8=#?K46`A0ZM0("%
  6016. M8*D`A6&I]J"FA0*$`Z9AO4V`\!"I`)U-@*4.H``@$(/&`\8#Y@[F8>8#Y@/&
  6017. M8-#=&&"@#HP`_ZD"H`"%#H0/A&"$8:4.I`\@=(2$`@4"T`;F8-`"YF'F#M`"
  6018. MY@^E#LU(@*4/[4F`D-NN/H`&8"9ARM#YJ0"D8*9A8*5DI&4@_X*0`6"I]J"<
  6019. MA6"$8:D`A6*M/H`*A6.E9*1E('2$A62$9<`%D!RM)8"%8JD!KCZ`X`'P`JD#
  6020. M+2:`T`6N)8#P`H5C(#6&8*8/T`(88"#)_Y`$C2"`8*G_IF/0`J5BA6:@`+%@
  6021. M)`X0!JJ]]HCP`R#2_\C$9M#L&*5@96:%8)`"YF$XI6+E9H5BL`+&8Z5B!6/0
  6022. MQ"#,_QA@H`Z,`/^%#H8/K2.`K"2`A62$94RAAB#JA9`!8*5ER060]!A@A0Z&
  6023. M4*D`A5&%5(558*D`A6*%8Z94\`2I`!A@IE`@QO^0!(T@@&"I_Z93T`*E4H5F
  6024. MH``@((>0`6#P!Y%@R,1FT/&$9ABE8&5FA6"0`N9A&*5B96:%8I`"YF,XI5+E
  6025. M9H52L`+&4Z54T`:E4@53T+@@S/^E8@5C&&"E5/`!`*51\`BI`(51J0H88*55
  6026. M*4#P"*G_A52I`!A@(,__II"&59`'C2"`(,S_8"0.$`RJO?:)\,?)#=`"A5&B
  6027. M_QA@I5K-2("E6^U)@)`!8*5:I%L@=(2$`@4"T`:E6J1;&&#F6M#<YEM,8X<@
  6028. M8X>0`6"%"80*I60%9?`2I62D9:8)AF2F"H9E()*$3,>'K2>`K"B`A0*$`Z`:
  6029. MI0F1`H5DR*4*D0*%9:G_H`^%"80*I62D92"2A*5DI&4@+8.0`6`8I5QE8H5<
  6030. MI5UE8X5=D`+F7AA@H`Z,`/\@J8:I`(5DA66%6X5<A5V%7JD"A5JI]J"<A6"$
  6031. M8:T^@`JHJ0"%4H13(+:&D`%@\`8@C(>0X&"M)X"L*("%`H0#H@"@'+5<D0+(
  6032. MZ.`#D/8@TX088*`.C`#_A@^JJ0`@J8:I]J"<A6"$8:D`H`2%4H13(+:&L!+P
  6033. M$*GVH)R%8(1A(#6&L`-,6(A@H`Z,`/^J(,;_D`RI`(TL@(TI@(TJ@&`@Y(@@
  6034. MY(@@Y(B-*8`@Y(B-*H"I`(TL@"#DB,DB\`C)0M#U(,S_8*``(.2(R2+P!IDM
  6035. M@,C0\XPL@"#DB,D@\/F-*X`@Y(C)`-#Y(,S_8"#/_[`%))!P`6!H:"#,_TR+
  6036. MB```````````%`D-`),`````````````````````````("$B(R0E)B<H*2HK
  6037. M+"TN+S`Q,C,T-38W.#DZ.SP]/C]`P<+#Q,7&Q\C)RLO,S<[/T-'2T]35UM?8
  6038. MV=I;7%U>7\!!0D-$149'2$E*2TQ-3D]045)35%565UA96MO<W=[?````````
  6039. M````````````````````````````````````````````````````````````
  6040. M````````````````````````````````````````````````````````````
  6041. M```````````````````````````````````````````````````````)````
  6042. M#0````````@``````````````"`A(B,D)28G*"DJ*RPM+B\P,3(S-#4V-S@Y
  6043. M.CL\/3X_0&%B8V1E9F=H:6IK;&UN;W!Q<G-T=79W>'EZ6UQ=7E\`````````
  6044. M````````````````````````````````````````````````````````````
  6045. M``````````````````````````````````````````````````````````!@
  6046. M04)#1$5&1TA)2DM,34Y/4%%24U155E=865I[?'U^?P``````````````````
  6047. 2``````````````````````!^````
  6048. `
  6049. end
  6050.  
  6051. 7. THE FUTURE
  6052.  
  6053. Future improvements to this program would include implementation of MS-DOS
  6054. formatting, more file manipluation commands (such as Rename), re-writing the
  6055. user-interface BASIC program in machine language, and making a file buffering
  6056. facility for those people with only one disk drive.  However, I don't intend
  6057. to do much more to this program.  My intentions are to put this functionality
  6058. into a device driver for a new operating system (or at least, operating
  6059. environment) for the C-128.  Anyone else is free to improve this program.
  6060.  
  6061. =============================================================================
  6062. In the Next Issue:
  6063.  
  6064. TWO-KEY ROLLOVER
  6065.  
  6066. This article will examine how a two-key rollover mechanism would work for the
  6067. keyboards of the C-128 and 64 and will present Kernal-wedge implementations
  6068. for both machines.  Webster's doesn't seem to know, so I'll tell you that this
  6069. means that the machine will act sensibly if you are holding down one key and
  6070. then press another without releasing the first.  This will be useful to fast
  6071. touch typers.
  6072.  
  6073. The Second Rob Hubbard Music Routine
  6074.  
  6075.   In this article, the second Rob Hubbard music routine will be presented in
  6076. the same way as the first. Future issues will hopefully examine various other
  6077. music routines including various Martin Galway, Benn Daglish, Jeoren Tel,
  6078. and Manaics of Noise routines. Note: Unfortunately the author completes
  6079. university (and thus loses internet access) in August 1993.
  6080.  
  6081. DYCP - Horizontal Scrolling
  6082.  
  6083. DYCP - is a name for a horizontal scroller, where characters go smoothly
  6084. up and down during their voyage from right to left. One possibility is a
  6085. scroll with only 8 characters - one character per sprite, but a real demo
  6086. coder won't be satisfied with that.
  6087.  
  6088. Multi-Tasking on the C=128 - Part 2
  6089.  
  6090. This article will examine the actual code that makes up the multi-tasking
  6091. kernal in detail and include some example programs explaining it use.
  6092.  
  6093. The 1351 Mouse Demystified
  6094.  
  6095. This article will explain how the 1351 mouse works as well as provide an easy
  6096. to use interface in machine language for both basic and machine language
  6097. programmers.
  6098. ========================================================================END===
  6099.  
  6100.