//////// // //////// // //////// // /// /////// ///// ///// // // /////// ///// // // /// ///// // // // // // /////// /////// ///// // // // // / / // // // // // // // /////// // // // // //// // // // //// // /// //////// ///// // / // // //////// /// // ///////////////////////////// The Journal of the Commodore Enthusiast I s s u e 1 : May 17, 1996 P R E A M B L E We greet you to the first issue of disC=overy, the Journal of the Commodore Enthusiast. Our inspiration for launching this work derives from you, the ones who still hold our beloved 8 bit machines in high regard and respect. In honor of your committment to these classic platforms we have pledged ourselves to assemble this entire journal on modest C64 and C128 systems. It is our sincerest hope that you will find our efforts to be of interest and special joy. We thank you from the bottom of our hearts and look forward to forging a solid productive relationship with all of you. - Mike Gordillo, Steven Judd, Ernest Stokes, and the authors of disC=overy. A R T I C L E S O F O P E R A T I O N Article 1 : Mission Statement Our intent is to present useful information in order to enhance and preserve the knowledge base of the Commodore 8-bit domain, including, but not limited to, the Commodore 64 and Commodore 128 home computers. To this end, we shall require that every article contain what in our discretion should be a viable Commodore 8-bit hardware and/or software point of relevance. Likewise, each issue should include material that can both potentially enlighten the most saavy of users as well as the layman. We intend to complement and assist all others engaged in similar endeavours. We believe it is of paramount concern to stave off entropy as long as possible. Article 2 : disC=overy Staff The current staff of disC=overy, the Journal of the Commodore Enthusiast, is as follows: Editor-in-Chief : Mike Gordillo (s0621126@dominic.barry.edu) Associate Editor : Steven Judd (judd@merle.acns.nwu.edu) Webmaster : Ernest Stokes (drray@eskimo.com) We invite any and all interested parties to join us as authors, panelists, and staff members. Article 3 : General Procedures The Editor-in-Chief shall supervise the organization of each issue in regards to grammatical and syntactical errors, flow of content, and overall layout of presentation. The Editor-in-Chief and Associate Editor shall form a review panel whose function it shall be to referee literary work which the Editor in-Chief has deemed to be of advanced technical and/or social merit. The Editor in-Chief and disC=overy, the Journal of the Commodore Enthusiast, shall retain copyright solely on the unique and particular presentation of its included body of literary work in its entirety. Authors shall retain all copyrights and responsibilities with regards to the content of their particular literary work. Authors shall be required to submit their works to the Editor-in-Chief approximately two weeks prior to publication. Article 4 : Peer Review To the best of our knowledge, disC=overy shall be the first Commodore 8-bit journal with a review panel dedicated to uphold the technical integrity and legitimacy of its content. The Editor-in-Chief and the Associate Editor shall be responsible for the formation of the panel. The appointed panelists shall have the option of anonymity if desired. The panel shall review works primarily for technical merit if the Editor-in-Chief and the Associate Editor deem it necessary. Authors may be asked to modify their works in accordance with the panel's recommendations. The Editor-in- Chief shall have final discretion regarding all such "refereed" articles. Article 5 : Distribution Although we welcome open distribution by non-commercial organizations, there are currently two "secure" distribution channels available to interested parties. This journal may be obtained by directly mailing the Editor-in-Chief or via the World Wide Web at http://www.eskimo.com/~drray/discovery.html Article 6 : Disclaimers The Editor-in-Chief and disC=overy, the Journal of the Commodore Enthusiast, retain all copyrights regarding the presentation of its articles. Authors retain all copyrights on their specific articles in and of themselves, regarding the full legal responsibility concerning the originality of their works and its contents. The Editor-in-Chief and disC=overy, the Journal of the Commodore Enthusiast, grants the reader an exclusive license to redistribute each issue in its entirety without modification or omission under the following additional stipulations: - If distribution involves physical media and is part of a commercial, not-for-profit, or PD distribution, the maximum allowable monetary charge shall not exceed $4 1996 United States Dollars per issue unless more than one issue is distributed on a single media item (i.e., two or more issues on one disk), in which case maximum allowable charge shall not exceed $4 1996 United States Dollars per media item. All dollar values given assume shipping costs are -included- as part of the maximum allowable charge. - If distribution involves non-physical media and is part of a commercial, not-for-profit, or PD distribution, the maximum allowable charge shall be limited to the actual cost of the distribution, whether said cost be in the form of telephony or other electronic means. It is understood that distribution denotes acceptance of the terms listed and that under no condition shall any particular party claim copyright or public domain status to disC=overy, the Journal of the Commodore Enthusiast, in its entirety. The Editor-in-Chief and disC=overy, the Journal of the Commodore Enthusiast, reserve the right to modify any and all portions of the Preamble and the Articles of Operation. :::::::::::d:i:s:C=:o:v:e:r:y:::::::::::::::::i:s:s:u:e::1:::::::::::::::::::: ::::::::::::::::::::::T A B L E O F C O N T E N T S::::::::::::::::::::::::: :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: -Software Section- /S01 - "Explorations on IFLI" $d000 by Adrian Gonzalez and Mike Gordillo /S02 - "TRI-FLI: A new video mode 'abrewing?" $d000 by George Taylor and Mike Gordillo /S03 - "Heaven in the net, an unedited excerpt of IRC on #c-64" $d000 by Mike Gordillo /S04 - "A complete dissection of Gfx-Zone" $d000 by "XmikeX" /S05 - "A Beginner's Guide to the JCH Editor V2.53, NewPlayer V14.G0" $d400 by Sean M. Pappalardo /S06 - "An inside look at MODplay 128" $d400 by Nate Dannenberg and Mike Gordillo /S07 - "Some preliminary data on VDC timing" $d600 by Steven L. Judd /S08 - "Software analysis and reconstructive therapy, a historical view $dd00 on 'cracking'" by Pontus Berg /S09 - "A Quick Overview of CP/M" 0100h by Mike Gordillo /S10 - "The BIOS-R62a/ZPM3/ZCCP Commodore 128 CP/M 3.0+ Upgrade Package 0100h and a bunch load of utilities!" by Mike Gordillo -Hardware Section- /H01 - "BEYOND STEREO - The POWER-SID Model 2400 : It May Not be THX but it's Taking the Commodore One Dimension Closer" by Shaun Halstead /H02 - "The 8 bit Modplay 128 Board" by Nate Dannenberg /H03 - "Upgrading your C128's VDC memory to 64K" by Mike Gordillo /H04 - "The Metal Shop" with SMS Mike Eglestone :::::::::::d:i:s:C=:o:v:e:r:y:::::::::::::::::i:s:s:u:e::1:::::::::::::::::::: /S01::$d000:::::::::::::::::::S O F T W A R E::::::::::::::::::::::::::::::::: :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: Explorations on IFLI by Adrian Gonzalez and Mike Gordillo Adrian Gonzalez is a true Commodorian, a rare breed in his native country of Mexico. He has spent the past year or so converting .gif and .jpeg files into the IFLI format on the C64. His endeavours have allowed many a C64 owner to enjoy high quality images. > Adrian, before we start, can you give us some background on what IFLI is and > how it achieves such stunning images? Sure thing. A little multicolor bitmap mode and FLI mode background is in order too. Images in the 'regular' multicolor bitmap mode (MCBM from now on) are divided in cells or blocks that are 4 pixels wide and 8 pixels tall. The screen is split into 40 columns of these blocks horizontally and 25 vertically, giving a total of 1000 4x8 pixel cells or blocks. A pixel in one of these blocks can have any one of 3 colors common to the entire block, or the background color, common to the entire screen. A FLI picture is similar to a regular MCBM picture in that it has the same resolution (160x200), however, it is more flexible in terms of how many colors you can use in each 4x8 character block. This flexibility is achieved through complex timing tricks which I'd rather explain in a future article, but I'll give a very brief overview of what they make the VIC-II do. Basically, the trick is to fool the VIC chip into fetching the screen memory data on every rasterline. This screen memory data is responsible for 2 of the 4 colors available on each 4x8 character block in MCBM (bit pairs '01' and '10'). So this basically divides each 4x8 cell of MCBM into eight 4x1 cells, making the VIC chip fetch a different pair of colors from screen memory for each rasterline (each 4x1 cell). This trick gets rid of most of the restrictions that MCBM imposes, since now 2 of the 4 pixels in every cell can have any one of the 16 available colors. Chances are the colors needed for the other two pixels will be available from either screen memory, color memory or the background color. This flexibility, of course, does not come without a price: storage. This technique multiplies the amount of memory needed for screen data times 8, adding 7000 bytes when you compare with a standard koala paint file. Fortunately, the 8000 bytes needed for screen memory and the 8000 for the bitmap fit just right on one 16k bank (remember the VIC can only 'see' 16k at a time). If you are new to these software screen modes, I recommend you read the previous part again, especially because once you get the grasp of FLI mode, IFLI is a snap. IFLI mode is basically two FLI pictures being rapidly alternated to give the illusion of more colors. So where does the added resolution come in? Thanks to the VIC-II's hardware, it is possible to shift the screen up to 7 hi-res pixels horizontally, even when in MCBM. The trick in IFLI is to display one FLI picture for an entire screen refresh, then display the second FLI picture shifted one hi-res pixel to the right on the next redraw. The effects of this are not so obvious, so I'll illustrate with an example. Suppose you have the first line of FLI picture A and FLI picture B and it looks like this: First line of FLI picture A: | K | G | Y | O | . . . First line of FLI picture B: | G | G | Y | O | . . . When you alternate them, shifting picture B one hi-res pixel to the right you get: | K | G | Y | O |. . . | G | G | Y | O | ------------------------------ |K |KG|G |G |GY|Y |YO|O |. . . Where: K = Black pixel G = Green pixel Y = Yellow pixel O = Orange pixel The resulting line has pixels that are as wide as high resolution pixels, and that may have colors that are combinations of the c64's 16 standard colors. It is evident from this example that IFLI's strengths lie in reproducing images with smooth color shades. The downside of IFLI is that it flickers, and depending on the colors that are being alternated, the flicker can go from barely noticeable to a stroboscopic light show. With this in mind, however, this display mode can produce some of the most stunning images that have ever been displayed on our beloved C64. After this brief introduction I hope the interview will make a little bit more sense, so let's get on with it. > Adrian, I know you do the bulk of your conversions on other platforms for > the sake of expiediency. However, I'm more generally interested in how > the pictures get down to the C64 in terms of the actual display on the C64. Well, I start out by doing careful color analysis on my Amiga. This involves pre-processing the images in programs such as The Art Department Professional, in order to adjust the brightness, contrast, color and size of the source images. After that, I feed the images to a conversion utility I wrote that tries (as best as possible) to map the colors from the original image to combinations of the 16 colors of the c64, taking many factors into consideration, such as flicker. The conversion program has several settings, which have to do mostly with dithering and flicker reduction. It is not always perfect, and the problem of IFLI flicker is never truly defeated, but I'm generally satisfied with the results. > Yes, they are beautiful conversions, but I could never get them to display > with any other IFLI viewer. Since there is no standard data format for IFLI pictures I had to come up with my own. My ifli's load at $2000, then the code moves them to the higher memory and depending on how big they are, of course, they get depacked accordingly. >Argh, they are packed? Of course they are packed. When was the last time you saw two of them having the same size? :) > What kind of packer did you use? They are packed using a very simple RLE routine. If you want you can use the routines in my viewer to depack them for you. They'll set everything up in the right place. (e.g. $4000-7fff, $c000-$ffff and $d800-$dbff). If anybody is interested in these routines or the source code for the viewer, feel free to mail me and I'll send them your way. > Why did you split the data over those addresses? Is it safe to assume that > IFLI data is composed of two 16 kbyte blocks for pic data and then comes the > color info at $d800 on up? Yeah, you can look at it that way. It's actually two 8k blocks containing the bitmaps and two 8k blocks containing the screen memory. > Ah, I see. There is one FLI pic portion in each 8k bitmap and one 1k block for color memory that is common to both images. Yes, but you know, it might be interesting if it were possible to change color memory as well. It would give us a slight boost in the number of apparent colors. > Could you explain exactly how, and wouldn't any further memory tweaking > complicate matters more? Maybe you would need to not use the entire screen? Oh, just mumbling out loud :). In practice there is no time to change color memory on the fly. Also, it doesn't matter if you do IFLI's smaller than the entire screen (say 1/3 screen IFLI) because you still have data scattered all over the same ranges I described earlier. The IFLI data must be placed at certain spots in memory so that the VIC-II chip can properly fetch it. > Ok, I understand, but this 1/3 IFLI screen proposal intrigues me greatly. > I hear "less than full-screen" IFLI's may be doubled-over onto themselves, > thereby giving even more colors or perhaps leading to run-time IFLI > animations. This is a possibility, but you would have even more flicker than regular IFLI. I'm not even sure if the logistics involved will allow even a 1/3 IFLI to be doubled over. > Well, if the CPU proves too slow, what about using the speedy REU to do the > job? Interesting, the problem is that since I'm not lucky enough to own one, I do not know how much overhead it takes to set up the REU for multiple transfers. For example, if I wanted to transfer 512 bytes of screen memory, then reprogram the REU to transfer another 512 bytes, I would need to know how many cycles it would take for each request I sent to the REU and then fit it into the IFLI code. As I hinted at earlier, IFLI data (except for the bitmaps of course) is scattered in little chunks. > I purloined some standard pre-code from George Taylor. It sets up the REU > transfer with regards to screen memory, as follows: > > lda #0 > sta control ; to make sure both addresses are counted up > lda #<$0400 > sta c64base > lda #>$0400 > sta c64base + 1 > lda #0 > sta reubase > sta reubase + 1 > sta reubase + 2 > lda #<$0400 > sta translen > lda #>$0400 > sta translen + 1 > lda #%10010000; c64 -> REU with immediate execution > sta command > > Total cycle count is around 60 cycles in this case, but if you shave off > some formalities (direction, transfer length) you can cut it to 30 cycles, > as follows: > > lda # sta c64base > lda #>any64addy > sta c64basehi > lda # sta reubase > lda #>anyReuaddy > sta reubasehi > lda #$91 > sta command > So if you implemented a set up time of 30 cycles per transfer, would this > allow some IFLI doubling or animations? Hmm, you need 8 requests for screen mem + 1 for color + 2 bitmaps. So, 30 cycles just for the setup and then 1 byte per cycle transfer... that's quite good. I think even my most optimized copy routine for internal memory moves would not match this, at least for large transfers (you've gotta love DMA :-). > But even with 60 cycles or less, could the reu copy memory much faster with > all the little bits and pieces involved. Yeah, either that or use 2 mhz mode on a 128, which would not be something I'd like to do. I think the REU is a better vehicle for doubled-IFLI or even IFLI animations, although I do not think full-screen IFLI's could be coaxed into it. > Well, just shrink an IFLI pic to the dimensions of a sprite and double it or > animate it over several times :) Come to think of it, pc users watch video in postage stamp-sized windows :). But there are other factors to consider though, such as the effect of the picture alternation on the animation. The IFLI color effect could be lessened by an animation with a high frame rate. It would be a very good experiment, though. Who knows, maybe with the upcoming 20mhz boards we could even have an MPEG player for the c64 :). As for me, all I need now is somebody to donate their REU in the name of IFLI research :-). -- For more information, gripes, etc, Mr. Adrian Gonzalez may be reached at the following internet addresses: al170866@academ01.mty.itesm.mx agonzalez@nlaredo.globalpc.net /S02::$d000::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: TRI-FLI : A new video mode 'abrewing? by George Taylor and Mike Gordillo George Taylor can be described as one of the few hard-core Commodore 64 purist coders. His mission is to advance the applicable knowledge base and utility of the C64 by bringing to bear a deep understanding of the hardware and software behind it. It is often said that chance favors the prepared mind, but in George's case, the prepared mind leaves nothing to chance. > George, I am very curious about your proposed tri-fli technique. I hear > that it will "go beyond" what is currently available in terms of VIC-II > video on the 64. I would be honored if you would take the time to clarify > things for me and our readers. Basically, I have an idea that allows no flicker unlike true IFLI, while still increasing the amount of perceived colors on screen. Right now I am testing the idea with 4 interlaced hires (not FLI) screens. Unfortunately, the colors are so close together I can hardly tell them apart. In other words, I get 4096 colors but I can't even see them all. When I scale the project up, and work out the problems, it may evolve into what I call "tri-fli". > Sounds nifty. Let's backtrack a little though. Your current testbed is > 4 interlaced hires (320*200?) screens. Just HOW are you getting even this > set up to NOT flicker? You seem to think this part of it is difficult, Mike. :) > Yes, I do -sans flicker-. I'll be amazed if you can scale it up to the FLI > domain where timing and other concerns label the process to be difficult. Even if they were FLI, there would be no problem with timing.. > Over my head.. fli =! timing? Generally speaking, interlace switching occurs in the borders, so you get lots of time even on an NTSC machine. The idea is that -one- FLI screen takes careful timing, but interlacing several FLI screens is NO problem! > So your technique is basically IFLI revisited? Hmm... well..yes and no. I should clarify a few things :). > Please do, Professor Taylor :). Multicolor mode is a bitmap + a color map, FLI is a bitmap + * color maps. > I'm with you so far. Normally, each color map byte affects an -8x8- area. In FLI, the color map is changed each line with a raster. Thus, 8 color maps are used in 8 lines, and each byte of each color map now affects an -8x1- area. This is why FLI gives a better placement of colors. However, there is more to this. For example, to switch the color maps in fli requires a trick, otherwise FLI would be easy and not need any special timing. This trick is called a DMA retrigger (among many other names for it) and it is necessary because the VIC-II video chip normally only reads where the color map is every 8 lines. Therefore, if you switch the color map WITHOUT the DMA retrigger, nothing would happen. The retrigger requires a certain POKE if you will at an -exact- moment in time on each line in order to function. This is where most of the whines about FLI coding arise. Please note that the DMA retrigger takes up 43 cycles on each line it occurs, thereby adding to the timing woes. FLI is just four lines of code that are tricky to time :). > Ouch, but what exactly does the DMA retrigger do? It sounds like a sermon > into "bad-lines" theology would be required to explain it :). Hmm.. I can explain DMA later someday, somewhere :). For now all you need to know is that it is a trick to cause the color map location to be read on each scan line, instead of every 8 scan lines. > Hey, a layman reponse! I can deal with it. There's no point in being a beanie head if you can't interface with normal types, Mike :))))). I will take this opportunity to explain one pecularity about the DMA retrigger that will hopefully enlighten the hard-core as well as the laymen. When people learned of this DMA trick, they found that the first three bytes of the FLI picture were seen as garbage. To compensate, they left the left columns of the FLI as blank and to make the picture symmetrical, the same was done to the right columns. Thus, FLI pics are usually cropped significantly. I figured out just why the garbage bytes occur and by the same token, how to avoid cropping FLI pics in the future. Very simply, the colors shown during those first three bytes normally come from color memory, but due to a bug they are actually coming from the opcode being executed at that moment! Therefore, to fix the colors, you just need to write opcodes that represent the proper colors. I looked at it, and there is code you can write to make all 16 colors. > Wow...that is nice detective work. I'm sure somebody must have figured this out before, but I do not see it in the software I currently have. The problem is that most tricks are discovered by experiment and accident, few people actually know why they work. > The C= community tends to echo that. This is very true. It's due to talking to many others that I understand a lot of what is going on today. I put my knowledge and that of others and try to push the limits on this old tank (64) :). Let me tell you, it is as complicated as any computer, even more so. In order to fully understand the 64, you would have to study it as if it were the culmination of an advanced engineering degree. > I understand completely. Without the innovations pursued by a horde of > enthusiasts we would not be having this conversation today. But just how > are the advancements of the past being intergrated or improved upon in > your current project? Ok.. I wanted to finish the point about timing so you understand why even four screens is no big deal. Let's take a look at basic IFLI first and then contrast it with what I am trying to accomplish. IFLI is two screens of FLI, but alternating with each other like a continuous animation. Frame B follows frame A follows frame B follows frame A, etc., in an endless loop. The result is that the two FLI pics switch places with each other so rapidly that they seem to blend together to the human eye. Therefore an apparent increase in the number of colors is the prime result. The standard IFLI technique is fast enough to do this but not fast enough to eliminate flickering as the two pics are flipped. Please remember from before that -each- FLI screen requires careful timing for the FLI/DMA stuff but this is completely unique and independent to that one particular FLI screen. The other FLI screen is just the same thing. In other words, setting up each FLI is tricky, but alternating them is not a problem. No more, no less code or timing, just different screens, ok? > Yup, understood. Ok.. Now, regardless of what textbooks say about the persistance of vision being 1/30 of a second, you can see much higher flicker rates and therefore my idea of using more than two screens should flicker quite a bit! > Well that depends, George. The resolution of the object and how "bright" > the object is to begin with has to be a paramount concern in regards to > flicker reduction. The retina has three types of neurons and one of those > types is responsible for "edge-assessment" of objects. This is where > brightness (contrast, etc) and flicker-effects are born in addition to > persistance of vision arguments. Exactly, I use two ideas to reduce flicker, and they work. One idea is just what you hinted at, to reduce contrast between colors. Thus I choose colors that have the same brightness but different tints, so only the color is changing. > The other idea is ? [...long pause...] This one is hard to explain. Imagine I am flickering between two colors, A and B. Typically, a standard IFLI pic will have them like this: For colors A and B frame 1: AAAAAAA frame 2: BBBBBB but I do this: frame 1: ABABABA frame 2: BABABA Think of it as marquee lights. The even and odd lights turn on and off. > Ah ha! You are decreasing the "granularity" of your interlacing on the > temporal axis ! ! ! Exactly!!! You understand :))))))))))) This technique has even more de-flickering power than choosing colors of the same brightness, and together both methods work even better. Now that we have settled that, my idea for "tri-fli" is very simple. If I can make IFLI -not- flicker, then I should be able to squeeze in another screen. I am currently using a testbed using four hires (not FLI, IFLI) screens because I can more easily experiment upwards and downwards and see how much flicker I am getting. > Ok, so you have set up a baseline with four hires screens... and? I haven't seen any flicker yet.. and this is mixing four colors in real-time. But let me first specify -exactly- how to produce non flickering colors. Definitions: bitplane 0: the screen buffer which is shown on frame 0,2,4... bitplane 1: the screen buffer which is shown on frame 1,3,5.. 1: indicates foreground color in a 2 color mode 0: indicates background color in a 2 color mode flashcolor: a color which is not one of the standard 16 colors, produced by mixing 2 of the standard colors with this technique palette: consists of n standard color and n-1 flash color 3 color hires mode ------------------ Screen buffer setup: two frames are initialized, such that the bitmaps contain 0 (all background color). The color assignment is such that both background colors (1 per buffer) are the same. The other 2 foreground colors are the same also. An example would be black+white for each buffer. To plot color 0 (black): bp0: 00000000 bp1: 00000000 to plot color 1 (medium grey, flash color of black+white): bp0: 01010101 bp1: 10101010 to plot color 2 (white): bp0: 11111111 bp1: 11111111 7 color multi mode ------------------ both buffers have the same color maps An example would be black+dark grey+light grey+white. color 0 (black): bp0: 0000 bp1: 0000 color 1 (dark dark grey, flash color of dark grey+black): bp0: 0101 bp1: 1010 color 2 (dark grey): bp0: 1111 bp1: 1111 color 3 (medium grey, flash color of dark grey+light grey): bp0: 1212 bp1: 2121 color 4 (light grey): bp0: 2222 bp1: 2222 color 5 (light light grey, flash color of light grey+white): bp0: 2323 bp1: 3232 color 6 (white): bp0: 3333 bp1: 3333 Note: although there are other combinations, such as: medium grey, flash color of white+black I do not reccomend them, as they produce nearly the same result yet with extra flicker. Flash colors should only be made in the nearest combinations of luminance. Colors are mixed nearly 50%/50% but not exactly. The wide pixels of multicolor mode increase flicker. Flicker is seen most at the edges of a flashcolor where the dither pattern is broken, and also when your eyes move. The sensors of your eye which detect movement will see the trailing patterns caused by the low frame rate of the effect, and this breaks the dither as well as lets you see the flicker. What about a four color hires mode? You would have 1 standard and 3 flashcolors, yet you can't dither them properly. For example color 1 would require: bp0: 01010101 bp1: 10101010 with bp0 colors=black+medium grey, bp1 colors=black+light grey. The result will be : abababab where a= dark grey, flash color of black+medium grey, b=dark medium grey, flash color of black+light grey. So you cannot create a constant shade, only a normal dithered one. You could create a constant shading with: bp0: 00000000 bp1: 11111111 Yet remember that this combination is not 100% flicker free. An alternative is in modes where you can change the palette at some other resolution, for example in hires IFLI you could draw a constant shade of flashcolor plus a 2 standard shades in any 8x1 area. For example, a byte with one half flash color and one half constant color is: bp0: 01010011 (1=white, 0=black) bp1: 01011100 (1=black, 0=white) to make this more clear: bp0: bWbWbbWW bp1: WbWbbbWW (b=black, W=white) with this result: 11110022 (0=black, 1=medium grey, 2=white) It is possible to have a horizontal resolution of changing shades averaging 8/3 pixels, for the example the above byte could have been 00011122 (0=black, 1=dark dark grey, 2=dark grey) and then: 01112222 (0=dark medium grey, 1=medium grey, 2=light medium grey) so you can make shading bands of up to 9 shades. > But how effective is the mixing? Does blue + green = bluegreen? As I stated earlier, I can now get 4096 colors but they are somewhat useless. The colors are only mixtures of the original 16 color and I can hardly tell the difference between blue-blue green and blue-green or green-green blue. Right now, I think the best use would be for grey shades. > Hmm.. You may have to bite the bullet and introduce colors that vary a little > in brightness? Well, the point of my whole idea is to eventually make standard IFLI to be non-flickerable. If that occurs successfully, I should then be able to interlace it yet again in alternative fashion and be left with minor flicker but with many more colors :). We shall see... -- For further information, gripes, etc., Mr. George Taylor may be reached via email at the following internet address: aa601@ccn.cs.dal.ca /S03::$d000::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: Heaven in the net, an unedited excerpt of IRC on #c-64 by Mike Gordillo As a self-proclaimed "demo freak", the following transcript (largely unedited) represents one of the most interesting discussions concerning C-64 that I have ever witnessed on IRC (Internet Relay Chat) or any other venue. I present this to the reader in the hopes of encouraging further participation and patronage of IRC channel #c-64. We begin in the middle of a dissection of VIC chip internals by Firefoot. > Can anything go on system-wise when a bad line is being "serviced" so to > speak? "BAD" lines are just another way of specifying the lines where the VIC steals 40 cycles from the CPU to do a screen refresh. You can delay the bad lines, and push the whole screen down (FLD). You can force them to occur every line (FLI). You can turn them off (blanking the screen). You can move them horizontally (VSP). The CPU isn't halted, it is busy helping the VIC chip out. Any code that you are executing is "halted". I never understood VSP? I thought the VIC brought the CPU off-line so it could sweep in all the data for the bitmap in those 40 cycles (for the next 8 lines). No, nothing can go on system-wise when a bad line is being serviced, not in terms of the CPU anyway. Wave - I guess that would be functionally identical. I guess it depends on whether or not you consider the bus part of the cpu. The VIC reads its data independent of the CPU, but...I still never understood VSP. =) > Yeah, but the CPU and the VIC chip can't share the bus at once. I never understood VSP well. There is something about how tricky stuff with $d011 can cause the VIC to think it has started a new scanline, when, infact, it has not. > Well..explain FLD for me then, Firefoot. :) FLD is easy but FLI is not as easy and VSP scares me. I've coded VSP, but never understood it either. However I do understand FLD. You just keep playing with $d011 (every scan line) so that the VIC keeps thinking that the *NEXT* scan line is the one where it is supposed to do the refresh of screen memory. when you stop doing this, it starts drawing the screen where it left off. Exactly, FLD is very easy to understand but what about VSP? Actually, I stumbled across VSP when coding FLI. FLI is essentially the same thing as FLD. Except that instead of making the VIC think the next scan line is where its supposed to draw the screen, FLI makes the VIC think the current line is the one to draw on - but you do it on every line. Well, wave, think of VSP as the same thing as FLD except instead of pushing the screen down scan lines, you push it across cycles. Also, try changing the delay at the beginning of an FLI routine, and you will see the screen shift over... voila, vsp! Well, I just recently got a stable raster (double interrupt style) so I haven't actually done anything that specific (FLI for example) though FLD is very forgiving. You can do nifty FLD with virtually no timing at all. > No timing for FLD? ok... Why am *I* having such a hard time putting > sprites over FLD then! FLI can be made very forgiving as well (no stable raster needed). Firefoot: It actually uses the CPU's Phi cycle as well as the VIC Phi cycle. Fire: That is probably what you are remembering. Well, with FLI it *is* nice to use a stable interrupt. I use the double raster method as well, always seemed the cleanest to me. Oh, it is not easy to put sprites over FLD. You have to make sure that your delay each line is exactly correct. very weird code... I did that and sprite over FLI with almost the same routine. ugh. Wave: That's *exactly* what I am remembering. Fire: the phi thing? > When the FLD bounces down...there -seems- to be a screen area behind it! Mike, basically what I did is constructed a section of unrolled FLI/FLD code, and played with the delay instructions each time I moved the sprites so that the timing was always correct. Wave, yes, the phi thing. From the appendix about the vic chip. It was very informative. > Ok...I've heard these $xfff addies pop up over and over and I guess they > explains why the some of my pics have those lines behind the FLD! Mike, the area behind the FLD is taken from the last byte of the video bank ($3fff, $7fff, $bfff, or $ffff). It is also what you see when you open up the borders. > Gawd, those annoying lines... I couldn't figure out where they were coming > from. Make VICBASE+$3fff equal to 0... or is it $FF?... and the lines will vanish. > I also ran a few old demos..and someone (tfo?) was blabbing about how this > border was opened up via FLD and the other one wasn't..etc., etc? How does > FLD open up a border? <[Style]> Is it true it becomes +$x9ff when extended color mode is on? Style, I have never heard that, but I have never used extended color mode while doing any of those. Mike, the "fld opening up the side borders" thing basically uses the stable raster created with the fld to open the side borders. easier that other methods, like the double raster method, but sloppy, in my opinion. besides, you can't get text or gfx.... > Firefoot, that is all well and good..but "How do you open up the nice > sideborders?...period"... Apparently, you are telling me that the FLD > is used as an "index" in this case. Oh... well... Do you know how to open the top/bottom borders (the theory behind it)? Well, if you time it right, you make the screen 38 columns instead of 40 right at the last cycle on the raster. > Only thing I know how to do is 38/40 column it... :D Then on the next cycle, the vic thinks it has already started displaying the border... so it doesn't start. <[Style]> Which location is the gfx behind an open side border??? Waveform: I've tried to do that, failed miserably. Style: VICBASE + $3fff Anytime you open a border or open screen space and the vic doesn't have normal info to fill it will it takes the last byte in its address space and sticks it in there. Croth: Did you make a stable raster first? Without that you will fail miserably. Firefoot: I couldn't figure it out. Had a friend of mine explain it to me, he was a genius when it came to that. :) > What about TWO FLD's? I could swear I have seen one FLD bounce and then > another bounce the screen behind it! Is this lunacy on my part? No it isn't, you stop one FLD...The VIC starts drawing...then later on, you start another FLD... You get two open spaces on your screen! <[Style]> And if you can be bothered, you can make a FLD on every line & slice the picture up :D FLD make the whole screen data move down and up. You can achieve similar effects with sprites... just make all the Y coords go up and down. > Ok..can you limit how WIDE the FLD is ? Or is it always full-screen? You can't FLD on every line... can you? doesn't that pooch your pic? I've seen every eight lines... but every line?!?! I thought that if you FLD after the VIC draws on the next line, you get a STRETCH, don't you? <[Style]> Wave, check out "Finely Sliced" by Christopher Jam. Mike, I think the FLD is always fullscreen wide. I can't see how it wouldn't be. Same here Wave, I've only seen every 8 lines as well. Perhaps you could do some sort of weird cross between FLI and FLD to get every line. Of course, the timing would be most annoying. Wave, I've seen that, but it seems to "stretch" every two lines? like that one arson demo, forget the name, and the one foe demo, and a few others as well. [Style], is "Finely Sliced" NTSC fixed? I think I accidentally did a stretch when trying to code FLD and it seemed like it did every two lines as well...weird. > Indulge me, what is a "stretch" ? <[Style]> Maybe it is every two lines.. Its been a while since I've seen it. I think Albion made a demo called ache! that did pretty much the same thing. <[Style]> Fire: I dont know if it is NTSC fixed... Mike, a stretch is an FLD with some slight modifications so that it "stretches" the screen data. [---end of transcript---] /S04::$d000::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: A complete dissection of Gfx-Zone by XmikeX The Graphics-Zone demo was released August 31, 1995. It was received with open arms and despite its simplicity, its unabashed unorthodoxy allowed it to reach the third highest spot in the Dec95. NTSC demo ballots. Gfx-Zone and the other two files included in the gfx-zone.sfx package were the culmination of ten days of self-taught assembly programming. I could not have done it however without the guidance obtained from the following sources; - Coder's World 1,2, and 3 by 'Wrongway and The Phantom', FOE Press :) - 128 Machine Language for Beginners by Richard Mansfield - Mapping the Commodore 128 by Ottis R. Cowper - Inspiration from 'The Last Digital Excess Demo', 'Skeletor Movie', 'Digital Acid', C=Hacking - Issue #7, the people on the IRC channel #c-64, 'SYS4096' music by AMJ, and Thinktank who tragically passed away months prior to the demo but had released his amazing Commodore/Graphics low-res (40 by 25) artwork to the public a few years before. Graphics-Zone is not an overtly arrogant and complicated demonstration. It consists of 21 Commodore/Graphics (i.e., low resolution PETSCII) full-screen drawings (that happen to be *well-drawn*) placed in synch with a very nice SID tune running in the background. There is little if any 'Top Code' in the demo and since I wrote it as a beginner, there should not be anything exceedingly fancy in the code itself that would preclude its use as a training vehicle for those not fully skilled in 6502 ML. In fact, there are some half- baked methods in the code that are rather simplistic even for a beginner. With this in mind, I hope to share some of the triumphs and pitfalls so as to instill a motivative spirit in those who seek to learn, and perhaps render a smile in those who already know :). As a great deal of you have already seen, running Graphics-Zone is a matter of loading it and typing "run". The first two screens pop up to form the intro sequence where two C/G pictures are flipped back and forth slowly to match the tempo of the music. When the music speeds up, the intro pics are abandoned for the most part and the rest of the C/G pics in memory fly at you in a mad attempt to keep up with the tempo. After a minute or two of this, the music and pics loop back to their start sequences and begin anew. There is nothing mind shaking about it. The whole demo is basically a planned sequence of memory moves for the pics with an interrupt (IRQ) driven music player behind it. The main block of C/G pics are located from $3000 to $bfff with three additional pics at $0800-$0fff, $2800-$2fff, and $c800-$cff0. Each C/G pic occupies 2 kilobytes (KB), the first KB is the screen data while the last KB represents color memory, as follows: ; Memory Structure - C/G pictures ; ; -------------------------------$3000 - 0 kilobyte, start of picture 1 ; ! char data for C/G pic 1 ; -------------------------------$3400 - 1 kilobyte, picture 1 ; ! color data for C/G pic 1 ; -------------------------------$37ff - 2 kilobytes, picture 1 ends ; ; ; -------------------------------$3800 - 0 kilobyte, start of picture 2 ; ! char data for C/G pic 2 ; -------------------------------$3c00 - 1 kilobyte, picture 2 ; ! color data for C/G pic 2 ; -------------------------------$3fff - 2 kilobytes, picture 2 ends In actuality, the screen and color data do not extend right up to the next page boundary (i.e., at the $xxff - $xx00 junctions in hex), they in fact end at $xxe7 (e.g., $33e7, $37e7, $3be7, $3fe7, etc). For simplicity's sake, I did not worry about the extra bytes at the end, and I started the each block of pic data (screen/char or color data) at a page boundary $xx00. But C/G pics are simply petscii text and color, right? Yes, they do not come organized as shown above and for a short time, the prospect of extracting them into a more usable form seemed daunting. After a few minutes, the solution came to me. I read them off the disk and printed them to the forty column screen on my 128. When this was done, I peeked an image of VIC screen memory ($0400-$07e7) and VIC color memory ($d800-$dbe7) and poked the image to $3000-$33e7 (screen mem) and $3400-$37e7 (color mem). Then I binary-saved them to disk using the monitor (via the 80 column display of course). I repeated this step twenty-one times because I was fortunate enough to have twenty-one quality C/G pics at my disposal :). In retrospect, I later found out that certain "taboo" areas of memory such as the area under the kernel could have been used to store even more data, but at the time I was quite happy to have been able just to switch out Basic and use the space it took up. What about the rest of the demo, the music and code, eh? Not a problem there. The music player/data starts at $1000 and heads up to around $26b0 while the main code for the demo itself starts at $c000. I took all the converted pic files (the ones I had binary-saved) and used a packer to relocate them to their final destinations in memory, along with the music and code. Also, because packers conveniently compress all data and code into a run-time executable, I didn't have to worry about providing a front-end ability to be able to "RUN" the ML from basic. The packer took care of all this for me, all I had to do was specify a start address for the machine-language (ML) code. The following is a disassembly of the ML code itself. Although I won't go through it 100% step by step, important points shall be perused for the purposes of clarification, and to be honest, for a little self-introspection on my part. The program starts out quite simply with the start address and a labelling of important memory locations (generally used as pointers throughout the program). ;--------------------------------------- *= $c000; start of program ;--------------------------------------- point = $fb ; LSB = point ($fb) ; MSB = point+1 ($fc) temp = $cfff ; MSB byte storage temp2 = $cffe ; used by FLD routine temp3 = $cffd ; $cffa/cffb for TBB/AMJ player uses these. looper = $ccfc ; see "pause" subroutine hit = $ccf9 ; see "pause" subroutine ;--------------------------------------- init lda #$36 ; #$36 is our kill-basic value, so sta $01 ; store the kill-basic value into $01 and move the ; basic rom out of the way, exposing the ram underneath. [AssEd. Note : For general edification, location $01 works as follows in C64] [ - bit 0: ROM/RAM at $a000 1=Basic 0=RAM] [ - bit 1: ROM/RAM at $e000 1=Kernal 0=RAM] [ - bit 2: ROM or I/O block 1=I/O 0=ROM] [ - bits 3,4,5 are cassette related........] [ - bits 6 & 7 are not connected in the C64] [ - bit 6 checks the status of the caps lock (ascii/cc) key on C128.] lda #$00 ; ok...set up accumulator as #$00 sta $d020; change screen and sta $d021; border to black (i.e., #$00) sta point; store LSB of pointer (which is now #$00) lda #$00 ; initialize the looper sta looper Ok... So what is going on here? Basically, we are telling Basic to take a hike so that we can use the memory it once inhabited. We are also setting up the LEAST SIGNIFICANT BYTE pointer for the indirect Y function. This is a powerful tool in 6502 assembly and we will get to it later :). The "looper" is just a memory location that the program will use in its "pause" subroutine. Right now, it is set at zero (#$00). UGLY lda #$fa ; try and tell tbb/amj music player sta $105b; what scanline to play at amjtune jsr $1000; jsr call to sys4096/amj's TBB player at $1000 This part is really UGLY. Basically, I tried to extract the music player and tried to incorporate it here, but I failed for some unknown reason. I decided that since it was proving to be uncooperative that I should just call it from here and let it go off on its own. This presented two problems for me later. The first was that the music was playing at raster lines that were outside the border area. In layman's terms this means that it would play in the middle of the screen and a slight flicker could be seen as the main code flipped through the pics. I rectified the situation by finding out where it was polling its raster info ($105b) and sticking an #$fa in there. #$fa is raster line 250, which should correspond to the lower border on the screen. Yes, I could have modified the player code itself, but I was getting a bit paranoid at this juncture and decided not to modify it. The second problem is that by giving up control to the player subroutine, I lost control of timing, an annoyance that which we shall discuss later. Anyways, as you can see I call the player in the 'amjtune' subroutine and let it do its thing, but even though it is not in the main code, I've included the player here for the benefit of the reader. As this code is from someone else, I cannot assure a 100% correct disassembly, but read on.. TBB player from SYS4096 tune by AMJ starts at $1000 and proceeds as follows: (By the way, TBB is AMJ's brother...trivia mode over). > sei ; disables interrupts > lda #$01 > sta $d01a ; set up for scan line > lda #$7f > sta $dc0d ; enable timer interrupts > lda #$35 ; lets play with roms > sta $01 > lda #$00 > ldx #$00 > ldy #$00 > jsr $1100 ; jsr to musix init ? > lda #$37 ; let's play with roms > sta $01 > lda # sta $0314 ; stash it > lda #>irq > sta $0315 ; MSB of irq > lda #$3a > sta $d012 > lda #$1b ; normalize the screen i think > sta $d011 > cli ; re-enables interrupts > rts ; it used to jmp back to itself, infinite loop > ; as its irq routine played in the backgroud > >irq lda #$01 ; the irq routine > sta $d019 > lda #$35 ; let's play with roms again > sta $01 > dec $d020 > jsr $1103 ; $1103 (!) I think this jsr's to musix data > inc $d020 > lda #$37 ; let's play with roms yet again > sta $01 > inc selfmod+1 ; self-modifying code!!! >selfmod lda #$xx ; #$xx = this is the byte changed by 'inc selfmod+1' > and #$01 > tax > lda #$105b,x ; goes to the scan line table ? > sta $d012 ; sets up the scan line for irq to occur > jmp $ea31 ; go to normal c= irq return From $105c to $10a0 or so beyond this there is some program data of unknown function. At around $1100 there is an embedded message by the authors and then the music data follows, ending somewhere around $26b0 if I recall correctly. Please note that this tune is double-speed (i.e., plays twice per frame). The initialization steps have executed and the music player has been told to start playing. What is left now is to present the C/G pictures to the viewer in a meaningful way. The main1 routine that follows conducts the sequence of the C/G displays. Its responsibility is to load and store the MOST SIGNIFICANT BYTE of the address (location) of each starting pic for a given pattern of displays and then branch out to the pattern subroutines, which display c/g pics sequentially given a predetermined sequence. Again, the MSB as with the LSB we encountered earlier deals with the indirect Y function that we shall explore later. main1 lda #$30 ; starting pic MSB sta temp ; store MSB in temp jsr pattern lda #$30 ; starting pic MSB sta temp ; store MSB in temp jsr pattern0 lda #$c0 ; this MSB is being stored but pattern1 does not use it sta temp jsr pattern2 jsr pattern1; fld bounce of the first intro pic only ; located at $3000-$37e7 ; fld effect is quite annoying, so its done only once lda #$38 sta temp jsr p0 ; p0 routine is a part (subset) of pattern0 routine lda #$c0 ; the rest of these are more of the same... calls to sta temp ; pattern subroutines jsr pattern2 lda #$38 sta temp jsr pattern0 lda #$c0 sta temp jsr pattern2 lda #$30 sta temp jsr pattern0 lda #$c0 sta temp jsr pattern2 lda #$30 sta temp jsr pattern0 lda #$c0 sta temp jsr pattern2 lda #$38 sta temp jsr p0 lda #$c0 sta temp jsr pattern2 lda #$38 sta temp jsr p0 lda #$c0 sta temp jsr pattern2 jsr pattern3; last pattern before music loops back on itself jmp main1 ; by now, music has looped...time to slow down again ; with the original pattern at the start of main1 routine ; (i.e., pattern routine that follows is called at the ; start of main1) "pattern" is the first of the pattern subroutines. It's responsibility is to take care of the first and second intro pics at the start of the program. It pulls the MSB from the temp memory location (the first intro pic), jsr's to the viewpic routine, and loads a "hit" value which the "pause" routine will then compare with an "looper" value. This determines how long the first intro pic will be shown. After which, "pattern" will change the MSB value (without affecting the MSB in temp) to point to the second intro pic, and then it repeats this process until the music tempo increases, upon which time "pattern" gives up control to another "patternX" subroutine. I mentioned earlier that I lost control of timing when I gave up some control to the music player code. That is to say, because I could not (at the time) incorporate the player code in here, I had no way of properly synching the pictures to the beat of the music as it played. I corrected this deficiency in true 'newbie' fashion. I used delay loops to form the core basis of what I could approximate as being a "beat" of music. The "pattern" subroutines use the "pause" subroutine (which includes the core delay loops along with "hit" and "looper" comparison) in order to determine how long a C/G pic should be displayed as the music plays in the background. I had to manually figure out how long it would take to go from its initial slow tempo to a faster tempo and then calibrate the "pattern" routine to give up control at the transition. pattern lda temp ; this sets up the initial -slow- flipping pattern of the jsr viewpic ; pics to match the -slow- tempo start to the AMJ tune lda #$11 sta hit jsr pause lda #$08 jsr viewpic lda #$11 sta hit jsr pause lda temp jsr viewpic lda #$11 sta hit jsr pause lda #$08 jsr viewpic lda #$11 sta hit jsr pause rts The other "patternX" routines and their subsets (p0, p10, etc.) basically perform addition or subtraction operations on the MSB they initially pull from the temp location. By doing so, you can display a number of different pictures in sequence with a minimum of effort. Recall that the pics are saved into memory initially with some organization behind it. That pre-planning combined with addition (adc) or subtraction (sbc) operations allowed me to display pictures in the order I desired. For example, assume that picture 1 is entitled "Boy", picture 2 is entitled "meets", and picture 3 is entitled "Girl". "Boy" is at $3000, "meets" is at $3800, and "Girl" is at $4000. The MSB represents the first 2 digits of the hex addresses I have just given, so "Boy" MSB is $30, "meets" is $38, and "Girl" is $40. Notice that each of these MSB's is precisely #$08 hex numbers apart! Since we haven't gone into the indirect Y function yet this may be a little premature, but it is logical to assume that if we had an indexing system in place all we would have to do in order to move from picture to picture would be to either add or subtract #$08! So basically our pictures are 8 units apart, for simplification as follows: lda 30 : Our house is at 30 main street :) jsr viewpic : Ask a photographer to photograph and display our house adc 8 : Inform the photographer that the next house is 8 blocks down jsr viewpic : Ask the photographer to photograph and display -that- house In actual code, if I wanted the pictures to come out sequentially as Boy meets Girl (remember the addresses we specified above) it would be lda #$30 : tell viewpic that "Boy" is at $3000 (#$30 = MSB = first two : digits of the hex number). jsr viewpic : display "Boy" clc : CLC - Clears the Carry Flag.. required step before addition adc #$08 : add another #$08 to the "Boy" address..#$30 + 08 = #$38 : in other words, the address for "meets" which is $3800. jsr viewpic : display "meets" clc : required adc #$08 : add another #$08 to the "meets" address..#$38 + 08 = #$40 : in other words, the address for "Girl" which is $4000. : REMEMBER, we are adding in HEXADECIMAL... jsr viewpic : display "Girl" Running this routine (and for now, don't worry about how viewpic works) within this program would allow us to display "Boy meets Girl". Simple, eh? The basic concepts hold for patternX routines that use subtraction except that instead of CLC, you are required to do a SEC before a subtraction operation. You will notice that the patternX routines jsr to the delay loop (e.g., loop1) routines more directly than the "pattern" routines. This is because "pattern" required a much longer delay and this is why "hit" and "looper" were created. pattern0 lda temp jsr viewpic jsr loop1 lda #$08 ; Load oddball MSB jsr viewpic jsr loop1 p0 lda temp ; Load MSB from temp clc adc #$08 ; Add #$08 to it sta temp ; Store MSB in temp jsr viewpic jsr loop1 lda temp ; Bring back MSB cmp #$b8 ; Is the MSB at the bne p0 ; final pic? $b800 rts pattern1 lda #$30 jsr viewpic jsr fldmain jsr loop2 rts pattern2 lda #$c8 ; Load 2nd oddball MSB jsr viewpic jsr loop1 p10 lda temp sec sbc #$08 sta temp cmp #$30 beq p10 jsr viewpic jsr loop1 lda temp cmp #$28 bne p10 rts pattern3 lda #$c8 jsr viewpic jsr loop1 lda #$78 jsr viewpic jsr loop1 lda #$a0 jsr viewpic jsr loop1 lda #$40 jsr viewpic jsr loop1 lda #$38 jsr viewpic jsr loop1 lda #$30 jsr viewpic jsr loop1 lda #$08 jsr viewpic jsr loop1 lda #$60 jsr viewpic jsr loop1 lda #$a8 jsr viewpic jsr loop1 lda #$68 jsr viewpic jsr loop1 lda #$70 jsr viewpic jsr loop1 lda #$80 jsr viewpic jsr loop1 lda #$90 jsr viewpic jsr loop1 lda #$88 jsr viewpic jsr loop1 jsr loop1 jsr loop1 jsr loop1 jsr loop1 ldx #$30 jsr d1 rts Ah, here we have reached the famous "viewpic". You will notice it doesn't do much. In fact, all it does is store the MSB pointer for the indirect Y function and "passes the buck" so to speak to the display routine. :) viewpic sta point+1; Store MSB pointer jsr display rts WARNING : INELEGANT timing solutions up ahead...be afraid, be very afraid... --------- pause jsr loop1 inc looper lda looper cmp hit bne pause lda #$00 sta looper rts loop1 ldx #$fd jmp d1 loop2 ldx #$01 jmp d1 d1 ldy #$ff d2 dey bne d2 dex bne d1 rts "loop1" encompasses "d1", and "d2". The whole scheme is a loop within a loop, with the idea being to be able to get "loop1" to approximately equal one beat of the music playing in the background. After about 30+ (!!!) recompiles, I got it almost perfect on an NTSC machine. The music plays 17% slower on a PAL (european, australian) machine and so this demo is horribly out of synch in PAL. For those of you who are wondering, Mr. George Taylor calculated the delay loops to be about 2.8% slower on a PAL machine when taking into consideration the slower PAL CPU and the penalties incurred due to "bad lines" (when the vic chip steals cpu cycles on the bus). As mentioned earlier, the "pause" routine encompasses everything "loop1" has to offer and extends the delay even further. The "loop2" routine would seem to be useless but it is called by the fld-bounce routines. The fld takes longer than a regular display so I figured I would only need to call a delay routine that was a fraction of a music "beat" (which is what "loop1" tries to be). Now we get to the real nitty gritty of the whole thing. The "display" routine and its subsets. These make use of the indirect Y function which Mensch and company thankfully chose to implement in the 6502. But wait, we haven't really gone into the indirect Y, have we? Nope, because it is tricky to explain. Like with movies or sports events, you have to be there in order to get the feel for it. In general, it is an index system that uses a zero page pointer of your choice in order to jump around to this or that memory location. But that's too vague...let's really explore it. Do you recall that at the start of the code we defined a few labels and basically equated them as memory locations? We said that "point = $fb" among other things. That is our memory (zp) pointer for the Least Significant Byte (LSB) and in the "display" routine below you will see a reference to point+1 which is our memory (zero-page) pointer for the Most Significant Byte (MSB). An address in memory is made up of two bytes, namely the MSB and LSB. Think of the MSB as the first two digits and the LSB as the last two digits, as follows: $c000 = $c0 MSB + 00 LSB $00c0 = $00 MSB + c0 LSB The two together make up a 16-bit address and due to how the 6502 works, it is one reason why 64 kilobytes can be accessed directly (2^16 = 65536). The indirect Y function allows you to set up an MSB and LSB pointer in zero page (ie., the first 256 bytes of memory from $0000 to $00ff). Note : You can't set a pointer in locations $0000, $0001, and $00ff. The LSB pointer I chose was $fb and by definition, the MSB pointer is automatically one memory location higher than the LSB pointer, so my MSB pointer is at $fc (point + 1). The "display" routines make use of the pointer functions as an index to copy C/G picture data to screen and color memory. But how does it do it, right? The "viewpic" routine we encountered earlier sets the MSB to the MSB of the picture that is about to be displayed. So for the first intro pic at $3000 this would be an MSB of #$30. Our LSB was set to zero (#$00) at the start of the program. So we have an MSB of #$30 and an LSB of #$00 = $3000 address. "display" now sets the Y register to zero (ldy #$00), "pa1" is now ready to copy memory. NOTE : For all the examples shown, we will use the MSB of the first intro pic ($3000 = $30 MSB = first two digits). The rest of the program changes the MSB on the fly so that when the "display" routine is reached, new pics can be displayed. If the MSB didn't change beyond the simple incrementations you will see below, we'd be stuck with displaying one pic. display ldy #$00 ; "display" encompasses all the memory moves that follow ;--------screen data moves-------------- pa1 lda (point),y ; $x000 $x800 sta $0400,y iny bne pa1 As you can see "pa1" takes the LSB of the pointer and uses the Y register to increment it (with INY) and then uses the same increment to store values to screen memory (which starts at $0400). In long form, this routine is just doing this : LDA $3000 ; take the first byte from the stored pic in memory STA $0400 ; put the first byte to the first screen memory location LDA $3001 ; take the second byte from the stored pic in memory STA $0401 ; put the second byte to the second screen memory location LDA $3002 ; take the third byte from the stored pic in memory STA $0402 ; put the third byte to the third screen memory location The INY instruction can only increment 256 times before the LSB runs out and it starts looping back to zero, so what we do now is increment the MSB ! (Remember, the BNE instruction checks when Y is equal to zero and moves on to the next routine -- Also, remember that Y itself does not change the LSB pointer, it only adds to the LSB *value* during the loop. LSB pointer itself stays the same, which means we can use it in the next routine without resetting it - the one we do increment is the MSB - via the INC instruction). ldy #$00 inc point+1 The previous routine "pa1" filled the first 256 bytes of screen memory with the first 256 bytes of our stored pic (i.e., it copied memory locations from $3000-$30ff to $0400-$04ff). We have just increased our MSB by one (using the INC point+1 - remember point+1 = our MSB in zero page). "pa2" will now do the same thing as "pa1" except it is now working on the next 256 bytes (i.e., it will copy memory locations from $3100-$31ff to $0500-$05ff). pa2 lda (point),y ; $x100 $x900 sta $0500,y iny bne pa2 ldy #$00 inc point+1 The MSB pointer is incremented again... $3200-$32ff to $0600-06ff. Please remember we are using the MSB of the first pic in this example. pa3 lda (point),y ; $x200 $xa00 sta $0600,y iny bne pa3 ldy #$00 inc point+1 The MSB pointer is incremented again... $3300-$33ff to $0700-$07ff ($07e7) Please remember we are using the MSB of the first pic in this example. pa4 lda (point),y ; $x300 $xb00 sta $0700,y iny bne pa4 ;--------color data moves---------------- ldy #$00 inc point+1 Are we seeing a pattern here? :) The MSB keeps getting incremented so as to allow yet another 256 byte copy-fill to occur. But now things change a little since we will now copy color memory. Not a problem because when we first organized our pictures in memory we put color data "RIGHT BEHIND" the screen data!! Recall, screen data takes up the first 1 KB while color data takes up the last KB (2 KB total per pic). So what do we do? We keep incrementing the MSB until the end of the pic is reached, but now we redirect our copy to the VIC color memory area ($d800-$dbff). "cpa1" copies $3400-$34ff to $d800-$d8ff Please remember we are using the MSB of the first pic in this example. cpa1 lda (point),y ; $x400 $xc00 ; we are now in the second kilobyte sta $d800,y ; of our stored pic data in memory.. in other words iny ; this continual MSB incrementation has gone through bne cpa1 ; the first KB and has now hit the second KB where ; color memory for the pictures resides ldy #$00 inc point+1 ; increment that MSB yet again.. cpa2 lda (point),y ; $x500 $xd00 sta $d900,y ; copies $3500-35ff to $d900-$d9ff iny bne cpa2 ldy #$00 inc point+1 ; increment that MSB yet again.. cpa3 lda (point),y ; $x600 $xe00 sta $da00,y ; copies $3600-36ff to $da00-$daff iny bne cpa3 ldy #$00 inc point+1 ; increment that MSB for the last time! cpa4 lda (point),y ; $x700 $xf00 sta $db00,y ; copies $3700-$37ff to $db00-$dbff ($dbe7) iny bne cpa4 rts ; return back to original calling routine, whatever ; that may be! ;--------------------------------------- nullpic ldy #$00 ; this is useless, i never did anything with nullpic ; i wanted to expand this to build some kind of random ; pic or blank screen.. but i forgot about it.. ;--------fld routine-------------------- FLD is known as Flexible Line Distancing and the routine that follows is an amalgam of something The Phantom/FOE did in a Coder's World 3 article. I will refer the reader to that article and a more comprehensive analysis of FLD in C= Hacking Issue #7. In brief summary, FLD is basically a raster trick that bounces the whole screen up and down quickly without having to engage in moving screen memory or vertically scrolling the actual picture data. This illustrates a good point about coding on the C-64. The best "coders" may not necessarily be the ones who best know the 6510 CPU. Generally, at least in the 'demo world', talent is assessed on how well the individual knows how to properly abuse the ancillary chips VIC, SID, CIA, etc. The FLD technique from my standpoint was an exercise in trial and error. I sadly did not use a sine table to coordinate the bounce-effect. I basically sat there tweaking it left and right until it did what I wanted it to do on my NTSC machine. In other words, once I got it to bounce the first intro pic a few times and gracefully depart, I was content. On PAL machines the effect is quite skewed and is not recommended for young viewers in the audience as it is rather grotesque :). fldmain lda #$00 sta temp2 fld lda #$2a cmp $d012 bne fld lda temp2 cmp #$4f beq fldmain2 start ldx temp2 bounce ldy $d012 cpy $d012 dey tya and #$07 ora #$10 sei sta $d011 cli dex bne bounce ldy #$15 sty $d018 lda temp2 clc adc #$07 sta temp2 jmp fld fldmain2 lda #$1b ; recovers first scn sta $d011 ; row from fld trick clc rts This is it! The end of this article and the end of what I hope was an enjoyable experience for you. Part of the disC=overy project is to stave off entropy for as long as possible. The best way to do this is to convert more order out of chaos in our local domain - the world of 64 :). In effect, by increasing the interest and drive we put into these old tanks, we shift entropy and chaos to the rest of the computer world. Because the universe is a closed system, this is the best we can do and is perhaps a lost cause ultimately, but I'll wager no one thought we would get this far. XmikeX /S05::$d400::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: A Beginner's Guide to the JCH Editor V2.53, NewPlayer V14.G0 by Sean M. Pappalardo (Pegasus/RPG) The JCH (Jens-Christian Huus) Editor for the C-64 is probably the most advanced sound creator and music tracking program of the group of software I like to call "Next Generation" software. This is software that pushes the Commodore past the limits the original designers placed on the hardware. This program has two main functions: to create and play high-definition analog synthesized sounds, and to arrange these sounds in music tracks. The program uses the following features of the SID (Sound Interface Device - the "Sound Blaster" of the Commodore): Three simultaneous synthesized voices (viewed as tracks by the editor), the four basic analog waveforms the SID produces (triangle, sawtooth, pulse, and noise, hereafter referred to as "simple sounds"), ring modulation, and filters. The secret to the -editor's- ability to produce quality high-definition sounds is the rapid cycling of simple sounds through the SID, one right after another, so fast that your ears more or less fuse the sounds into one complex sound. For instance, a standard snare drum sound is made up of 5 or more simple sounds that get played so rapidly that you hear one sound. The whole process takes only .0026 seconds, which is how this technique can be accomplished. For reference, a digital sound is in essence the same idea, except you hear about 12,000 simple sounds per second as opposed to 360. The only problem is that digital sound uses vast amounts of memory (12K per second of sound), whereas a 3 or 4 minute JCH song takes only 4K, give or take a K or two. Clearly, JCH songs are the more economical choice. The -player- is designed to be called from another program exactly once per screen refresh. The standard NTSC screen redraws itself 60 times per second, and so the player expects to be called 60 times per second, with equal time between the calls. The number of simple sounds that get played per second can be doubled through what is known as a "double-speed" player. This simply means that the player gets called twice per screen refresh, or 120 times per second, so playing about 720 simple sounds per second. This is done to create higher definition sounds (like raising the sampling rate of a digitizer.) The drawback is that twice as much processor time per screen refresh is taken to play the tune as with the single-speed player. This technique has been applied in creating 3, 4, 5, 6, even 8-speed players, each one having a difference in sound quality, and a proportional increase in rastertime use. Mind you, the quality is only as good as the creator of the sound, as the sounds have to be constructed by the composer, and the faster the player, the more detailed the sounds must be to sound their best. It is a shame that we can't just use a digitizer at a very low sampling rate to capture sounds instead of having to engineer each one carefully! The Main Menu F1 - Load Tables or Music F2 - Save Tables F3 - Save Entire Tune F5 - Send Disk Command F7 - View Disk Directory F8 - Enter the Editor The F1 key on this menu allows you to load up one of three things: an unpacked tune you wish to continue working on (or to view), a set of tables that contain instrument definitions, or another NewPlayer (A routine that actually plays the music. Different versions have different features and drawbacks. For example, 14.G0 uses more rastertime than the 19.G0 player, and they sound close in quality. If I'm not mistaken, the tune in 19.G0 sounded better!) The F2 key allows you to save the current instrument definition tables from the computer to the disk drive, for use in other tunes, so you don't have to re-engineer all of the sounds again for each tune. The F3 key simply saves all of the instrument definitions and music data in one unpacked file for later editing or packing (to be used in a demo or otherwise.) My particular version of the editor saves 147-block files, but I understand other versions save 64-block files. The size you get should match one of these. The F5 key will allow you to send any valid DOS command to your disk drive. (Such as a Change Partition command for CMD device users.) The F7 key obviously displays a directory of the current disk/partition. The F8 key is used to go into the actual editing area. The Editor Itself Once you make the bold move into the editor, you will see a screen covered with lines and numbers. This is the editing area, and once you understand it, you will get to like it. (I was a beginner too, you know!) To start, the windows with the light grey "8000" and green dashes are where the sequence of notes in a particular tune is displayed. Under that, there are two more windows with sets of zeroes in them. (These are actually hex bytes and each has a specific purpose, depending on the specific NewPlayer in use. For the purposes of this documentation, I will assume you are using NewPlayer V14.G0 because that one seems to be most popular.) Each of the digits can be set from 0-9 and A-F. To describe all of the keys used in the editor: V - Toggles whether or not the cursor moves when editing any hex bytes. (Useful for testing different values while playing a tune.) This value is displayed at the bottom right of the screen. The C+ means that the cursor will move, and C0 means that it won't. - Toggles keyboard lock. When it's enabled, only the cursor keys can be used. This is useful for testing instruments out without screwing up anything you've entered. It's also good to keep your kid brother from destroying your tune while you're in the bathroom. When the lock is on, you will see two white stars at the top left of the screen. \ - Sets a marker in the three tracks at the block which the cursor is in. F1 - Begin playing tune from the marker set with the \ key. F2 - Use the computer like a piano, with the current instrument. (Cursor to the line of the instrument you want.) This is obviously useful for testing instrument sounds. F3 - Begin playing the tune at the beginning. F4 - Stop all sound. F5 - Switch between block arrangement and block edit. F6 - Delete a note in the current block, or delete a block. - Insert a note in a block, or insert a block when editing block arrangement. F7 - Increase octave. F8 - Decrease octave. D - Increase speed of tune. S - Decrease speed of tune. The speed value is displayed above the cursor toggle display. (Bottom right) It can range from 0 to 9, so you'll see S0 if the speed is 0, S9 if it's 9, etc. 0 is the fastest speed, and 9 is the slowest. The octave is displayed above the speed, and can range from 0 to 7. Hence you'll see O0 if the octave is 0, the lowest, O7 if it's 7, the highest, etc. Z - Toggle to the instrument parameter window. X - Toggle to the glide parameter window. -After using slash (/) to pop up the wave/pulse/filter window, L, colon (:), and semicolon (;) will bring you to the waveform, pulse, and filter windows, respectively. = - Go to the top of the tune. will bring you to the top and put the cursor in the dashes, if it's not already there. F - Fine tuning. This feature allows you to make miniscule adjustments to the pitch of each track, so as to prevent/create sound wave interference between tracks. The values that appear can be set from $00 to $FF. Press after each one to set it. C - Clear all. This will prompt you first, and upon a positive response, will erase the block sequences, and set all the blocks to whatever was in the first block (# 00.) X - Leave the editor and return to the main menu. (So you can save.) - While in a block, this will insert 16 ($10) blank spaces in that block at the cursor position. WARNING: Don't make any block longer than 5 screens (80 lines). If you do, it will corrupt when packed. NEVER make a block 8 screens long! (128 lines) This scrambles data from other blocks in RAM and destroys the tune. If you save it, you'll never be able to pack it, as the packer will crash. Even if you delete the long block, the data remains scrambled and the packer will still crash. You'll have to rewrite the tune from scratch if you do this. I recommend a 4-screen limit on blocks. (64 lines) A - Copy block sequence into buffer. Z - Copy buffer into block arrangement. . - Copy current block into buffer. I - Copy buffer into the current block. 1/2/3 - Toggle voice 1,2,3 on/off. - Fast forward (while a tune is playing. Hold it down.) The left window, which is longer than the other (and contains 8 bytes) is the instrument pointer window. The byte before the colon is the instrument number. Each row is a set of data for that particular instrument. The editor supports up to 32 ($1F) instruments. The first nybble in each row (the first digit of the first byte) is the Attack control. (Attack is how long it takes a sound to reach maximum volume.) The second digit is the Decay control. (Decay is how long it takes for the sound to go from maximum volume to the sustain volume, if one is set.) The first digit of the second byte controls the Sustain level. (The volume at which the note is held until it is released.) The second digit controls the Release of the sound. (Release is how long it takes the note to fade to silence after it has ceased playing.) A value of 9 or higher in this nybble will create an echo effect. All four of these nybbles will be fastest if they are set to 0, and slowest if set to F. (This also applies to the vibrato bytes, which will be discussed next.) To recap: 00:00 00 00 00 00 00 00 00 ^^ ^^ AS DR The next byte is the Vibrato Speed byte. The first nybble controls how quickly the vibrato cycles up and down. The second controls how long the note must be sustained before the vibrato kicks in. The following byte is the Vibrato Control byte. The first nybble controls how much the note is oscillated from its original pitch, with 0 being none and F being the most. The second nybble's function is still unclear to me, and changing the value has small but strange effects on the behavior of the player. Review: 00:00 00 00 00 00 00 00 00 ^^ ^^ Vibrato Speed Vibrato Control The next byte is the 'effects' byte. The first nybble has some applications that are unclear to me, but setting it to a '1' will keep the note played a constant pitch, no matter what note the track data says to play. (This is useful for drums as they have a constant frequency, except for tom-toms.) The second nybble controls how the filter information is to be applied to the sound. It chooses the high-pass, low-pass, band-pass, etc. filters.) A value of '0' in this nybble will shut off the filter for that instrument. The next three bytes point to filter parameters, pulse parameters, and the waveform sequence for this instrument, respectively. These bytes are set depending on where in the waveform, pulse, or filter tables you wish the sound to get its data from. (Explained in a moment.) Review: plse ^^ 00:00 00 00 00 00 00 00 00 // ^^ ^^ fx filter waveform Now, press the slash (/) key. You will now see a new trio of windows, the leftmost of which is the waveform sequence. The bytes preceding the colons are reference numbers, which are used in the afore mentioned waveform pointer byte. The same idea applies to the middle window which is the pulse parameters, and the right window, which is for the filter parameters. The 2-byte waveform sequence lines each represent a simple sound. It is in this sequence where all the magic of detailed synthesized sound occurs. The first byte controls how much the pitch of that particular sound is offset from the actual note listed in the track data (That is, if the first nybble of the effects byte is not set to 1, otherwise these sounds are offset from a constant note.) A value of 00 makes the pitch exactly equal to the listed note, and as the number increases, so does the pitch. (You can't offset the note downwards.) The first nybble of the second byte selects the waveform of this particular simple sound. Values are as follows: 1 = Triangle 2 = Sawtooth 4 = Pulse 8 = White Noise Other values would create interesting sounds. The second nybble controls the ring modulation and sound nature. For instance, a value of 0 in this nybble will cause the simple sound to take the resounding volume from the previous sound and use that to make the current sound, as opposed to putting a 1 in this nybble, which will cause the sound to generate its own volume, and "stand alone", if you will. Other values control the usage and nature of ring modulation. offset Review: ^^ 00:00-00 00:00 00 00 00 00:00 00 00 00 // ^^ ref# ^fx waveform Now, to explain the pulse byte settings (Second window from the left in the pop-up set. By the way, you can use the slash key (/) to toggle these windows on or off, if you decide you wish to see more of the tracks.) The leftmost byte is the maximum value that the pulse will reach or cycle to, depending on the counting direction nybble. The nybbles are switched in this byte (as well as in the rightmost byte, which is the width at which the pulse wave will start cycling from.) So if you place E3 into either of these bytes, the pulse width represented is $03E0. The second byte from the left controls the speed of the wave. Often, interesting waves can be constructed if you set this to a high value, resulting in some wave interference, like out-of-phase standing waves. Finally, the first nybble of the third byte is the counting direction nybble. Different values will cause the pulse value to stay steady at the start value, go up to the max value, go up then back down, infinitely oscillate, etc. The second nybble controls the amplitude of the sound wave. Remember, you must have some of the waveform data contain a 4 in the first nybble of the second byte, because that 4 tells the player to use the pulse values specified by the pointer in the instrument data window (seventh byte.) See how all this ties together? The filter byte settings are exactly the same as those for the pulse, except they control the filter (obviously.) Remember that in order to use a filter, you must have placed some number (other than 0) in the second digit of the fifth byte in the instrument data window, and have set the pointer (sixth byte) to point to the filter parameters you wish to use for that particular instrument. Review: speed amplitude ^^ ^ 00:00-00 00:00 00 00 00 00:00 00 00 00 // ^^ ^ ^^ ref# end dir start Regarding the main track windows, the first three dashes are for commands that will be acted upon when the music reaches that point. These commands can be one of the following: instrument change, note slide, or legato. To change the instrument, simply type an 'I' in the first position, then type the number of the instrument you wish to change to. (The number preceding the colon in the lowest, longest window.) To use the note slide (or "glide",) type an S in the first position, then the number of the glide parameter you wish to use at that point (explained later.) The slide will affect only the note at that position. (It can be extended to other notes if the legato is used.) To use the legato, simply type a star (*) in the first position, and move off of the line. (The star will change into three.) Place this in front of each note that you wish to continue from the previous one. The second set of dashes is where the actual notes are placed. All you have to do is cursor to the desired position, and use the top two rows of the keyboard as a piano keyboard in entering the notes, as follows: 2 3 5 6 7 9 0 - \ - black piano keys Q W E R T Y U I O P @ * ^ - white piano keys ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ C D E F G A B C D E F G A B Pressing and while the cursor is on the second set of dashes will put a sustain flag into that note position. (Much like a sustain pedal. It is represented by three plus signs +++.) The number after each note is the octave, which can be raised using F7, and lowered using F8. Review: 8000 --- --- Inst.#00- I00 C#4 - Note: C sharp (#), octave 4 --- +++ - Sustain Slid.#00- S00 +++ - Sustain same note, acted on by slide --- +++ - " --- --- - Nothing (release of previous note) --- D-4 - New note, same instrument --- +++ - Sustain Legato- *** E-4 - New note, sustained from previous --- +++ (no attack/decay) The glide parameter window is the rightmost one on the bottom of the screen. (And it can be reached by pressing X.) The numbers before the colon are the reference numbers for each of the glide definitions. The first byte controls the direction and extremity of the slide. The second byte controls the speed. A wide range of slides is possible with these -two- bytes, which can each be set from $00 to $FF. (That's #$ff * #$ff = 65,536 combinations!) Editing the arrangement of blocks is fairly easy. The light grey numbers have certain significance: The first two digits act as a separate byte controlling the transpose value of the block. In other words, that byte will increase the key of all of the notes in the block by a half-step for each number increased. (Don't decrease the number...it has strange undesirable effects on the editor or the sounds.) The second pair of digits is the number of the block. This can range from $00 to $72. Don't go any further, or you'll get strange undesirable effects again. (This is probably due to the fact that a certain amount of RAM was sectioned off for blocks and if you exceed that, you scramble other parts of the tune/program.) To end your tune, place a block at the end numbered $FF00. (This is usually done automatically by the editor.) This number causes the player to repeat the music either from the beginning or from the set mark, depending on whether you have pressed F1 or F3 to start the tune. To create more than one tune in the same player (conserves space) all you need to do is make the tunes one right after the other, then insert a block numbered $FF00 between each of them, and save. Well, that should be plenty to get you started and on your way to making tunes! If you have no desire to create sounds yourself, you can take them from other JCH tunes very easily by using one of the sound ripper utilities, or using a depacker and then saving the sound data tables, provided you have the same player that the depacked tune was written for. The best thing to do is to set up some basic sound definitions, and basically play around with some of the values, such as the pulse and filter parameters. Once you train your ears to break down complex sounds in our world into combinations of the four simple sounds, you will be able to recreate those sounds in the JCH editor. In some cases, you may desire or need a multiple-speed player. All you need to do is pick up a one-block program that alters the editor to play in multiple speed. (You may even be able to alter it yourself.) Enjoy! ----- The following is a uuencoded file of Sean's first JCH tune; begin 600 NewBeginning M¿0@K"¿¿¿ES@P."PR,C4ZES>P,#¿P)K'QY[¿FLC(GL":QR¿@S=3(GL#¿P,#¿P,#¿¿!0)!¿"9 M(I&9P,#¿P,#¿P,¿%Q\K&QLF9P¿74RL;)F<#¿!=3*QLO9F<¿%U-G-("#9F<#¿ MP,#¿P,#¿¿$\)!0"9(I$>P,#¿P,#¿P,"5RL;&R<@>P)74U<3+'L#¿E=35Q,G9 M'L"5U-D>P)7-(-D>P,#¿P,#¿P,¿¿C¿D&¿)DBD9K¿P,#¿P,#¿P,#¿P('(V9K¿ M@:P('(R)K¿@U<;&R\BR,B4"¿%02!,()Y!()92()Q$(!Q/¿'(*"@"9(A$@'%"64D6>4T5.!513 M($].(#4OGC(Q+Y8Y-!PZ(!*7)YA!FR¿%3D57($)%1TE.3DF;3IA'ER<¿I¿H+ M¿)DB("¿@("¿@("¿@("¿@("¿@("¿@("¿@("¿@$II!GR¿%1U)%050@5%5.GT6: M(0#9"@P¿F2(1'BV9/05214Q%05-%1"!/3B!42$4@1$%9($E4(%=!4R!#3TU0 M3$541429/1XM¿!<+#0"9(A&<1Y9219E%5$D%3D=3(%1/FSH@ETH>19]&F48@ M!4)!F4.?3QY,ET\@*!Q:GTF<4AY#'S";*2P¿4@L.¿)DB("¿@("¿@("¿%4I9! M0QQ(14P@2$5,1T664T\%3ILL(¿5!GTX?1T5,3R!"24%.0Y](!4F;+¿"4"P\¿ MF2*1("¿@("¿@("¿%5)E/'DT@2T$>39E)!4Z;+"¿%19E$GU>:29=.G"!6'T&5 M3B¿<4X%%EDZ;5)Y%!4Z;+¿#*"Q¿¿F2)!3D0@04Q,($]&($U9($]42$52($92 M245.1%,@04Y$($-/3E1!0U13(CJ>,S¿W,@#A"Q$¿GC,P-S4Z@4&R,*0V.H(Z MB3$W¿¿¿¿_P#_¿/\¿_P#_¿/\¿_P#_¿/\¿_P#_¿/\¿_P#_¿/],0¿Q,V@P!¿@0/ M0#/K:1@5$0'\/C0P¿¿¿¿¿/[^_@¿¿("U03$%915(@0ED@2D-(+BXN+DU54TE# M($)9(%E/52$M"@H*J*(¿N>\3G4X3G503N?¿3G5$3G5<3R,CHX¿/0YZ(¿CDT3 MN>\3C0L,C7L3C7P3C7T3G=D3[DT3K0L,&'GO$XT+#!AM31/HX!#0YZT@#/¿K MH@*Y\!.-31,]6A.=!@S*$/$L31,0%:(¿N?$3G503N?(3G5<3R,CHX¿/0[:¿¿ MF)D¿U,C¿&]#XJ)E^$YEF$\C¿#-#UK0D,C1C48*("O6D3R0+0++QR$[EC%+Q@ M$YD%U+QR$[ED%+Q@$YD&U*U*%/¿)K4D4F0343¿\-O7@3F034RA#*I?M(I?Q( MH@*]!@S0¿TR¿$KUO$_¿(WF\3T!-,O0Z\!,I_ID$U+QR$[EC%+Q@$YD%U+QR$[ED M%+Q@$YD&U+UX$YD$U$P$#[UC$YT:#+U^$YT4#+V!$YT7#+W*$YW-$YUI$[UL M$_¿#3)H/O&¿3K4<4F074F0;4K4H4\¿NM210I_ID$U$P$#[UX$RG^F034O'(3 MF)T=#+EJ%)V^$[EI%)VK$ZBI¿)VN$[E5%"F¿R8#P#[E6%$@I\)VQ$V@I#YVT M$[QR$[EG%*¿¿*0_P&,D(\!,*"@H*#0D,C1C4R*T*#!U:$]¿'R*T*##U=$XT7 MU(T*#,¿!T!Z\KA-,\¿^\JQ.Y4Q1(2DI*2IV?$V@I#YVB$[E4 M%)VE$[E5%(U-$RD_"IVN$RQ-$Q¿4N5842"GPG;$3:"D/G;03J0"=J!,L31-P M!Y@8:02=JQ.]J!/0&;VQ$QA]I1.=L1.]M!-I¿)VT$]VB$]¿A\!>]L1,X_:43 MG;$3O;03Z0"=M!/=GQ/0"+VH$TD!G:@3X¿#P¿TS'$*V\$_¿&SKP33)H0K+L3 MN4<42"GPC;<3:¿H*"@J-N!.Y2!2-N1.Y212-31,I/PJ-O!,L31,0)*T*#"D/ MC4T3N4H42"GPC;T3:¿H*"@H-31.-"@R-%]2I¿(VZ$RQ-$W¿'F!AI!(V[$ZVZ M$]¿1K;T3&&VY$XV]$\VX$Y¿9L¿^MO1,X[;D3C;T3S;<3L¿BMNA-)¿8VZ$[QR M$[EG%"GPR1#0'[R^$[G_$\E_T¿JY(Q2=OA.HN?\3G0\,J0"=#¿Q,,1&\OA.Y M_Q,P$,E_T!*Y(Q2=OA.HN?\3$¿8*H¿%,&Q$8?10,"AA]%PR@¿(Q-$ZBYC1(8 M?=83G0P,N8X2:0"=#PR\OA.Y(Q2=>!/^OA.]S1/P4[W'$]¿6O=¿3&'W!$YW0 M$[W3$WW$$YW3$TQP$;W0$SC]P1.=T!.]TQ/]Q!.=TQ.M31/0$[T,#!A]T!.= M#¿R]#PQ]TQ.=#PR]:1/)¿?¿#WFD33%82O6D3\#[)¿?¿&WFD33%82O'(3N684 M2DI*2IV'$SCI¿9V$$ZD¿G9¿3G983G9D3N6442"GPG9P3:"D/"IV3$]YI$TQ6 M$KV'$_!YWI,3,¿-,5A+^DQ.]#PQ*2DJ-"PR]G!,8;0L,G8H3J0!I¿)V-$]Z$ M$Q¿.O9¿320&=D!.]AQ.=A!.]D!/0%KV6$QA]BA.=EA.]F1-]C1.=F1-,0Q*] MEA,X_8H3G983O9D3_8T3G9D3O0P,&'V6$YT,#+T/#'V9$YT/#+Q@$[VQ$YD" MU+VT$YD#U*V]$XT6U+T,#)D¿U+T/#)D!U+UX$ST:#)D$U,HP¿TP:#6B%_&B% M^V¿6¿2<&!,:H1M&'00?W"#0(MPD$"=>*=8K,.@@^N$&@1;A)($Z\4JQ7Y%QP8DQHA&X8=1!\<(-¿BW"30)QXI5BO MR+G@Q)C0"-TPZB#X+OT¿N@1/&!D9M@%,&!D9¿0($_OW[¿¿<._O[^¿¿L"¿¿¿¿ M¿0$!¿¿¿¿¿¿¿@$Q,_04$A#P,'-#¿¿¿¿¿¿__\¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿ M¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿&!@¿$¿1V@X,¿"@H(¿+¿/¿90:W@$!%0¿¿¿¿¿¿ M¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿!¿@,'"P\3%QL?(R'R¿A M(B,D)24F)R@H*2D!¿00!!@$("¿H*#¿P.#!¿0$A+_@¿*¿¿@("¿@("¿@("¿@(" M¿@("¿@(3$Q,3$Q05%146%Q<7&!D9&1H3$Q,3$Q05%146%Q<7&!D9&1HP,3(P M,S¿T,34Q-C$W-C@Q.3'_"QDI+CM*5V9S@H^>J[K'UN/R_PXF/55LA)NSRM3; MY/'Z!R¿Y4FMXA9*KQ,G.T=37VN#EZ_'W_0,)#Q86%A86%A86%A86%A86%A86 M%A87%Q<7%Q<7%Q<7%Q<7%Q@8&!@8&!@8&!@8&!@8&!@8&!@8&!@9&1F@@#"$ M¿(¿WA¿"¿-(0¿?Z""¿(¿TA¿"¿,(0¿@#>!¿'^/¿($¿?X¿PA¿"¿-X0¿@#6$¿'^" M¿(¿UA¿"¿,(0¿@#>!¿'^¿,(0¿@#>$¿(¿RA¿!_@@"¿,H0¿@#"$¿(¿W@0!_@"V$ M¿(¿TA¿"¿,H0¿?X(¿@#*$¿(¿MA¿"¿-($¿?X¿MA¿"¿-(0¿@#&$¿'^"¿(¿QA¿"¿ M+80¿@#2!¿'^¿*80¿@#"$¿(¿MA¿!_@@"¿+80¿@"F$¿(¿P@0!_@"F$¿(¿PA¿"¿ M*X0¿?X(¿@"N$¿(¿IA¿"¿,($¿?X¿KA¿"¿,H0¿@#"$¿'^"¿(¿PA¿"¿*X0¿@#*! M¿'^¿*X0¿@#*$¿(¿OA¿!_@@"¿+X0¿@"N$¿(¿R@0!_I(¿8A¿"¿&($¿@!B!¿**¿ M&($¿I(¿3@0!_I(¿8A¿"¿&($¿@!B!¿**¿&($¿@!B!¿'^D@!6$¿(¿5@0"¿%8$¿ MHH¿5@0"D@!"!¿'^D@!6$¿(¿5@0"¿%8$¿HH¿5@0"¿%8$¿?Z2¿$80¿@!&!¿(¿1 M@0"B@!&!¿*2¿#($¿?Z2¿$80¿@!&!¿(¿1@0"B@!&!¿(¿1@0!_I(¿3A¿"¿$X$¿ M@!.!¿**¿$X$¿I(¿.@0!_I(¿3A¿"¿$X$¿@!.!¿**¿$X$¿@!.!¿'^E@#R*¿(¿^ MA¿!_A0"¿0(H¿?X!!B@"¿0X0¿?X!!A¿"¿0(0¿@#Z$¿'^¿0(H¿@#Z$¿'^¿0(0¿ M@#R$¿(¿WA¿!_@#F!¿(!¿@0"¿/H$¿@$"!¿(¿Y@0"¿0($¿?X¿^@0"¿0($¿@#F! M¿(!¿@0"¿/H$¿@$"!¿'^¿.8$¿@$"!¿(¿]@0"¿0($¿@#F!¿(!¿@0!_@#V!¿(!¿ M@0"¿.8$¿@$"!¿(¿]@0"¿0($¿?X¿UB@"¿-8$¿@#6!¿'^¿-80¿@#>$¿(¿\A¿!_ M@#6$¿(¿YA¿"¿/(0¿?X¿W@0"¿/($¿@#Z!¿(¿W@0"¿/($¿@#Z!¿'^¿-X$¿@#N! M¿(¿^@0"¿-X$¿@#N!¿(¿^@0!_CP"!¿'^/¿($¿?X¿¿?X¿¿?X¿¿?X¿¿?Z://(%^ M?X]^@7Y_IX\\@7Y_J(\\@7Y_IX\Y@7Y_IH\Y@7Y_IH\U@7Y_J(\U@7Y_IX\W M@7Y_IH\W@7Y_&AH:&AH:&AH:&AH:&AH:&AH:&AH:&AH:&AH:&AH:&AH:&AH: M&AH:&AH:&AH:&AH:&AH:&AH:&AH:&AH:&AH:&AH:&AH:&AH:&AH:&AH:&AH: 9&AH:&AH:&AH:&AH:&AH:&AH:&AH:&AH:&AH: ¿ end