magazines:discovery1
//////// // //////// // //////// // /// /////// ///// ///// // // /////// ///// // // /// ///// // // // // // /////// /////// ///// // // // // / / // // // // // // // /////// // // // // //// // // // //// // /// //////// ///// // / // // //////// /// // ///////////////////////////// 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 #<any64addy > sta c64base > lda #>any64addy > sta c64basehi > lda #<anyReuaddy > 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? <firefoot> "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). <firefoot> The CPU isn't halted, it is busy helping the VIC chip out. Any code that you are executing is "halted". <Waveform> I never understood VSP? <Waveform> 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). <firefoot> No, nothing can go on system-wise when a bad line is being serviced, not in terms of the CPU anyway. <firefoot> Wave - I guess that would be functionally identical. I guess it depends on whether or not you consider the bus part of the cpu. <Waveform> 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. <firefoot> 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. :) <Waveform> FLD is easy but FLI is not as easy and VSP scares me. <firefoot> 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. <Waveform> Exactly, FLD is very easy to understand but what about VSP? <firefoot> Actually, I stumbled across VSP when coding FLI. <Waveform> 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. <firefoot> 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! <Waveform> 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! <firefoot> FLI can be made very forgiving as well (no stable raster needed). <Waveform> Firefoot: It actually uses the CPU's Phi cycle as well as the VIC Phi cycle. <Waveform> Fire: That is probably what you are remembering. <firefoot> 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. <firefoot> 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. <firefoot> Wave: That's *exactly* what I am remembering. <Waveform> Fire: the phi thing? > When the FLD bounces down...there -seems- to be a screen area behind it! <firefoot> 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. <firefoot> 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! <firefoot> 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. <Waveform> 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? <firefoot> Style, I have never heard that, but I have never used extended color mode while doing any of those. <firefoot> 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. <firefoot> Oh... well... Do you know how to open the top/bottom borders (the theory behind it)? <Waveform> 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 <Waveform> 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??? <CRoth> Waveform: I've tried to do that, failed miserably. <Waveform> Style: VICBASE + $3fff <Waveform> 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. <firefoot> Croth: Did you make a stable raster first? Without that you will fail miserably. <CRoth> 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? <Waveform> 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 <Waveform> 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? <Waveform> 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. <firefoot> Mike, I think the FLD is always fullscreen wide. I can't see how it wouldn't be. <firefoot> 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. <firefoot> 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. <firefoot> [Style], is "Finely Sliced" NTSC fixed? <Waveform> 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... <firefoot> 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). <start of player code> > 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 #<irq ; LSB of irq > 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 <end of player code> 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). <back to main program code> 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. <Commodore Key> - 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. \ - <Used while the cursor is in a track> 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. <INS> - Insert a note in a block, or insert a block when editing block arrangement. F7 - Increase octave. F8 - Decrease octave. <SHIFT> D - Increase speed of tune. <SHIFT> 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. <CLR> will bring you to the top and put the cursor in the dashes, if it's not already there. <SHIFT> 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 <RETURN> after each one to set it. <SHIFT> 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.) <SHIFT> X - Leave the editor and return to the main menu. (So you can save.) <SHIFT> <RETURN> - 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) <SHIFT> A - Copy block sequence into buffer. <SHIFT> Z - Copy buffer into block arrangement. <SHIFT> . - Copy current block into buffer. <SHIFT> I - Copy buffer into the current block. <CONTROL> 1/2/3 - Toggle voice 1,2,3 on/off. <back arrow> - 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 // ^^ ^ ^^ <same for these> 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 - \ <HME> - black piano keys Q W E R T Y U I O P @ * ^ <DEL> - white piano keys ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ C D E F G A B C D E F G A B Pressing <SHIFT> and <RETURN> 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<W-2PP.I<U,S(X,"PP.I<U,S(X,2PP.IDBDP!E M"À$ÀF2(<P,#ÀP,#ÀP,À?U<3%Q,D<P!_5Q,7$R1SÀ']7$Q<3)',À?U<2[',À? MU<D<P,#ÀP,#ÀP,ÀÀHÀ@"À)DBD8'ÀP,#ÀP,#ÀP)K'U<3$RX'ÀFL?5Q,3+@<": MQ]7$R<B!P)K'(,V!P)K'R('ÀP,#ÀP,#ÀPÀ#;"À,ÀF2*1GL#ÀP,#ÀP,#ÀFM3' MGL#ÀP,":Q\>>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@<?'FL#ÀP,"!Q\>:P('(R)KÀ@<?(FL#À@=3(FL#ÀP,#ÀP,#ÀÀ,D)!P"9(I&< MP,#ÀP,#ÀP,">U<;&R\B<P)['RL;&R9SÀGL?'G,">R,B<P)['R)SÀP)['R)SÀ MP,#ÀP,#ÀPÀÀ&"@@ÀF2*1'\#ÀP,#ÀP,#À!<K&TL;+'\À%RL;2QLL?PÀ7*RQ_À M!<K+'\À%RLL?P,À%RLL?P,#ÀP,#ÀP,ÀÀ-ÀH)À)DB("À@("À@("À@("À<4""< M02"64"">4"À%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\<A.Y9A0I#]U[$_À&WGL33)H/G6\3 MO743G7L3O4X3A?N]41.%_*ÀÀF)UL$['[$À\*G8$3_DX3TÀ/^41/(L?NHN9<5 MA?NYT16%_+QF$['[,"CP',E^\ÀZ=?A.]RA/P"=[*$TR7#?YL$ZG_G6,3T&RI M_IUC$_YL$]!B2"G@R8#0&6A(*1"=;!-H*0^HN=D3G7L3G743_F83T+')H-À, M:ÀH*"IUR$_YF$]"A:"D_"JBYM!2=P1.YLQ1(*1^=Q!-H2"FÀG<<3J0&=RA.I MÀ)W0$YW3$V@I(-#._LH3T,G^9A.\9A.Q^\E_T"VIÀ)UF$ZB]3A,8:0&=3A.% M^[U1$VDÀG5$3A?RQ^\G_TÀR]5!.=3A.]5Q.=41/)_MÀ.J0"=!@R\8!.9!-1, M@!*];!/P([UO$]À;O6,3G1H,O7X3G10,O8$3G1<,O<H3G<T3G6D33)H/O&À3 MK4@4F074F0;4O6\3\"],@!*\8!.]>!,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\<A.Y:!2-NQ.HN4D4*8#)@-À&N4H4C;T3J0"-O!.IÀYUI$ZU* M%-À#3,<0WFD33(À2O:X3\À;>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<!.À%+À5\!<P&*À:$!N@'4À?À!#@(MÀDX"<0*6ÀKT"YP(3ÀT(#=À.I MÀ^À#&P1:!)L$X@0L!7L%S@4G!H4&ZÀ91!\$'-PBT"#<)QÀE7"O4*GÀM.#ÀD- MTÀVC#H(/;A!H$6X2B!.O%.L5.1><&!,:H1M&'00?W"#0(MPD$"=>*=8K<BXX M,28T0C>,.@@^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<K+S,W.S\ÀÀÀ#\/À&V%À$5 M3!4#![84À15,%?__À'_?$@[?#'_?"@@&!0!_<!]P?PÀÀ?P!_ÀÀ0'?PÀ%!W\À MÀ@=_00"!$$"À$À6!$!À0$ÀÀ(@1&À$4$A%$$6(2$A&"$A(1PA(2$@#PÀ)À0IÀ M@@0ÀÀÀÀÀÀÀ#_"ÀÀÀ_]\ÀÀ@):ÀÀÀÀÀ"4IÀÀÀÀÀÀ@À#N4ÀÀ!ÀÀÀÀ@("ÀÀÀ$ÀÀÀ MÀ@54ÀÀ(1!À0//_@ÀÀÀÀÀÀ!,/J0ÀÀÀÀÀÀ%@53ÀÀÀÀÀÀÀ8!5,ÀÀÀÀÀÀ!P%4PÀÀ MÀÀÀÀ(ÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀ@À"ÀÀÀ,À!0À'!PD)"PL-"P\/$1$ÀÀÀ,À!0À'!PD) M"PL-"P\/$1$ÀÀÀ,À!0À'!PD)"PL-"P\/$1$ÀÀÀ,À!0À'!PD)"PL-"P\/$1'_ M@À&ÀÀ00!!@$("ÀH*#ÀP.#!À0$A(!À00!!@$("ÀH*#ÀP.#!À0$A(;'!T>'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 /S06::$d400::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: An inside look at MODplay 128 +-+ by +-+ Nate Dannenberg and Mike Gordillo Nate Dannenberg is an expert at digitizing sounds on the Commodore 64 and 128. For the past year or so, he has been the heart and soul of probably the most revolutionary program in Commodore 64/128 audio since Mark Dickenson's Stereo Sid Player. MODplay is the name and it will rock your system to the core. You are the first priviledged readers to get a sneak peek at this exciting bit of software. -- > Nate, you are the author of the world's first and only .mod player on the > 8 bit Commodores. I am curious to explore some of its inner workings. > > But first... What exactly IS a "mod" anyways? Mike, you probably know the answer to that one already. A mod is simply a music file format which started on the Amiga line of computers and spread to many other systems. It is a very efficient format in which music is sampled or cut into discrete portions and later reassembled in many different ways to create varied tunes and patterns to form a musical score. The module format can be summed up as a form of musical archive. A few instruments may be sampled only once, but their samples can be manipulated in thousands of ways that may not be possible on the actual instruments. For example, a sample of a flute can form an entire orchestral piece in and of itself without much effort. However, in order to achieve this, a computer requires a moderate amount of speed and memory. Many have discounted the possibility of .mod playback on our machines and have therefore not explored the true limits of hardware in this regard, until now. > The C128 is not known for top speed and memory though. Fortunately, it has just enough speed and memory when combined with a 17xx Ram Expansion Unit (REU). To be perfectly honest, my modplayer can only function with an "REU" and a C128 running at 2 MHz. But it functions well enough to keep tempo with the majority (if not all) the 4 track "mods" out there and it can even output sound in 8 bits! In the future I may try to support the Ramlink/Ramdrive units and perhaps one or two of the more popular internal ram (256k to 1 megabyte) expansions. Although the Ramlink/Ramdrive units may not have enough speed for the task, I do believe the CMD Super 64 and 128 accelerators will change this and allow for potentially both C64 and C128 accelerated modplayers in the future. > But for many years, I have heard (from Amiga users primarily) that our little > machines did not have the CPU horsepower necessary to simultaneously process > and play back the module format. Amiga users cannot be wrong! How did you > manage to do it? You have to know a little about how the module format works, but without going into too much detail, imagine all samples within a .mod file as being organized into rows and patterns. These rows and patterns determine what, when, and how sample data gets played back to the user. In my player, rows and patterns are centered around their respective tracks (channels). The basic procedure is described as follows : Get the 16 byte row data from the ram expansion unit. Now divide the row into four 4-byte subsections where each subsection represents a track (1,2,3,4). Get the current 1 KB pattern data into memory and extract the next 16 byte stub from that pattern. Divide the pattern stub as the row was divided before (into four 4-byte subsections) to match each track. Within each four byte track, pattern information about a row is divided into a sample number, a note period, an effect command, and an effect parameter. The sample number directs the program to use a certain sample for whatever row (i.e., notes) are being played in that track. The sample number is derived as follows : "zeroth" high nybble byte + (second high nybble byte/16). It is then used to index a data table where info such as sample length and start address inside the ram expansion unit are obtained. The note period, on the other hand, tells the CPU how many 3.5 MHz ticks to count down before sending out the next sample byte. The note period is then used as a lookup into a frequency stepper table in order to determine the pitch of the note. A value of 254 for example would be the equivalent of concert pitch A-4. The effect command modifies the pitch, playback time of the note or the whole pattern, and the volume if needed. You can use it to apply an array of effects to each note, including vibrato, portamento, arpeggio, etc. The effect parameter in turn modifies the effect command. For example, if the effect command is a zero and its parameter is a zero as well, then any effect for that track will be cancelled immediately. Likewise, if the effect command is zero and its parameter is something other than zero, then the effect command is treated as an arppegio using the supplied parameter value. For reference, if any pattern value for a given row is zero then the pattern data for the previous row is used. After all is said and done, my program grabs all the module data in little spurts from the ram expansion unit at approximately 459,200 bytes per second leading to (after processing) an effective 8.2 KHz play-back rate. It is interesting to note that my modplayer could reach nearly twice the sample rate it has now if it were to pull its data from the 128's internal memory. Alas, there are few .mod files that could comfortably fit in 128 kilobytes. If I decide to allow the player to grab data from internal memory, it would probably be in support only for internal ram expansions. > How would internal ram be faster as compared to the ram expansion unit? The ram expansion unit (REU) is a marvelous blessing. It allows my program to naturally store huge .mod files and has the fastest transfer rates of any ram storage device in the Commodore 8 bit market. Despite this, it does carry overhead penalties that simply do not exist when accesing internal memory. For example, I must calculate a three-byte value into the ram expansion unit (LSB, MSB, RamBank) and copy it to the ram expansion address pointer and also into internal memory. I have to toggle down to 1 MHz while accesing the REU. I then toggle back up to 2 MHz while copying the module data into the C128 as I do a 4-level deep interleave on the module data itself. As this entire sequence is repeated many many times, you can see how much time is lost. If I were doing all this from internal memory, I would have to again calculate the three-byte pointer but this time I could keep it in zero page and would never have to copy it anywhere (except to copy the bank pointer to the memory mangement unit inside the C128). I would never have to switch to 1 MHz mode. Furthermore, I would not need to access the module data in small chunks as it would all be easily available in internal memory. As it stands now, data is cached into the C128 as described earlier for logistical reasons concerning ram expansion overhead times. A more direct method of accessing the module data would be simple with internal memory. > You mentioned that your modplayer outputs in 8 bits. Could you explain > how you squeezed 8 bit audio out of the C128. Originally, I used a technique called Pulse Width Modulation to do it. Remember that early 64/128 digiplayers used the $d418 reg. for 4 bit digis. I used $d402 instead as the DAC register while toggling the test bit in $d404 for each sample byte. I then filtered voice 1 and WHAM, instant 8-bits. The techinique is interesting and might be of use to you, as follows : Simply put, just set voice 1 to produce a pulse waveform for the current sample rate. This pulse will range from zero to maximum width. At the sample rate of my player, max. width corresponds to 127 cycles. Now start stuffing numbers into $d402. Each time you do that, put a hexadecimal 49 and then 41 into $d404 and be sure to filter voice 1 with a low pass..voila. Your timing needs to be good and steady. Needless to say, the whole operation works best in 2 MHz mode. Using pulse width modulation, the pulses can range from 1 cycle to about 127 cycles wide at the 8.2 KHz sample rate. That is equivalent to 7 bits and the filter gives you a boost in quality (i.e., effective 8 bit resolution). The one major drawback with the PWM technique is that it leaves behind a residual squeal that is audible at the current sample rate. I am currently working on a methodology to completely eliminate the squeal and the most promising option appears to be an extension of the PWM technique known as PCM or Pulse Code Modulation. There is also a minor problem with low volume output but this is easily corrected by amplification at the user's sound system. > These 8 bit techniques you mentioned are for use with the SID chip. Do you > have any other ideas in mind? If the SID had a volume register from 0 to 255, I could have used the standard $d418 DAC technique for 8 bit resolution. As this is not the case, PWM/PCM is necessary for the quality I hope to get out of the SID without resorting to additional hardware. I do provide standard 4 bit and dithered 4 bit sound output in the program and will fully implement 8 bit SID sound when I feel it has met my requirements. However, I have not discounted other options. As seen elsewhere in this first issue of disC=overy, I have gone into detail on an 8 bit DAC board that is very simple to build and program. The modplayer already supports this particular board as a low cost alternative. I am also working on a more advanced dual DAC -- ADC board that will be available for sale along with the modplayer. It will have four tracks, each of 8 bits and with an 8 bit linear volume control yielding an effective 16 bits of resolution per channel :). > Before we depart, I would like you to emphasize the difficulty in what you > have done, a C= 6510/8502 modplayer! :) The task was not terribly difficult in theory. In practice it has sometimes become quite a hassle and it is still in progress. If you want a comparison, for a no-frills .raw digi format (not the module format) you could probably get away with over 80 (eighty) KHz in simple 4 bit mono on a C128 at 2 Mhz and you can do about 60KHz 4 bit mono from the ram expansion unit. On a C64 you should be able to do at least 30 KHz mono 4 bits from the ram expansion and about 44 KHz from memory. Obviously, these would be short samples but the drive home point is that the module format is indeed a computation-intensive exercise when compared to .raw digis. Regardless, my player can handle up to 31 samples, up to 255 patterns, 4 to 8 bits, 4 tracks, and up to two megabytes of .mod file if you have the 2 meg REU expansion. I have not yet implemented the sliding routines yet, and its sample rate of 8.2 KHz, although not exceedingly high, is quite adequate for many .mod files. I do believe that the new SuperCPU 64 and 128 accelerators from CMD will allow for even greater sample rates and generally make my task easier overall. I will take a close look at them and at internal memory expansions. If I find these "vehicles" to be proper and good, I may support them in the modplayer. > I'd like to thank you for granting this interview, Nate. Well, just don't let it go to your head, Mike. :) > ;) -- For more information on this one of a kind product, you may reach Nate at the following addresses: Dannenberg Concepts, Ltd. "Bringing your Commodore 128 one step closer!" Email: tron@wichita.fn.net Phone: (316) 777-0248 Snail: Dannenberg Concepts, Ltd. c/o Mr. Nate Dannenberg 306 S. 1st Ave Mulvane, KS 67110 FidoNet: 1:291/25 Groups: CBM, CBM-128, and CBM-GEOS Usenet: comp.sys.cbm, alt.binaries.sounds.mods /S07::$d600::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: Some preliminary data on VDC timing by SLJ 1995/96 (comments -> sjudd@nwu.edu) ----------------------------------- With some very helpful commentary from Andreas Boose! The 80-column VDC chip on the C128 is one of the least documented features of the 128. All documentation I have found echoes the 128 PRG, so that the general features and programming of the chip is well known, but as far as I can tell there is zero information on timing, internal operation of the VDC, etc. In an attempt to gain some insight into the timing and operation of the VDC I wrote a series of simple experiments. Later I received some helpful suggestions and comments from Andreas Boose: any comments indented with a '>' are from him. My personal interest is in using the VDC as a graphics coprocessor. As anyone who has programmed the VDC knows, all communication with the VDC is done through two memory locations, $D600 and $D601, and entails the use of a busy-loop to determine when the VDC is ready to be read or written to. My idea was to have the 8502 doing something useful instead of sitting around waiting for the VDC, so that I could for instance do some calculations while the screen is being cleared. Attached are some simple programs I wrote to get a feel for VDC timing. There is an ML program to do the VDC operations, and a BASIC program to generate a little statistical analysis of data. Eight tests are performed and timed using CIA #2 timer A. In retrospect some are pretty naive, but I include them all for the sake of completeness: Test 1: This is a simple calibration test to determine how many cycles are used in starting/stopping the timer. It simply starts the timer, executes 5 NOP instructions, and stops the timer. By subtracting 10 cycles from the time elapsed, you get the overhead. Test 2: Load a register (Reg 18) -- This test simply times how long it takes to tell the VDC which register you want to operate on -- no reads or writes are performed. The idea is to calculate how long it takes the VDC to find a register. Note that the register is not loaded with anything -- by "loading a register" I simply mean making it read/write accessible. Test 3: Load a single register twice. My idea here was to see how 'smart' the VDC was, i.e. if it already had the register loaded, would it spend any extra time reloading it. In principle this should be twice Test 2 above. Test 4: Load all registers. This test starts at register 36 and goes down to zero. The idea here is to see if different registers take different amounts of time to fetch, i.e. will we just get 37 * Test 2 here? Test 5: Two reads of register 18. The whole point of this test is to see if _reading_ from a loaded VDC register affects subsequent loads in any way (the expectation is that it would not). In principle this test should be Test 3 plus an additional 8 cycles for the 2 LDA's. Test 6: Read a single VDC memory location ($1919). This is a test to see how long it takes to find and read a memory location within the VDC RAM. Location $1919 was used simply because the value $19 was already in the accumulator :). This one should take a little more time than three register accesses. Test 7: Loop memory fill. Using a loop on the 8502 side, this test fills 256 bytes of VDC memory, starting at location 0, using the auto-increment feature of the VDC. Test 8: Block memory fill. This test uses the block-write feature of the VDC to fill 256 bytes of VDC memory (0-255). Naturally the idea is to compare it to Test 7. Just to make sure the test 7 was doing what I wanted it to do I stored the VDC memory location after the loop, to make sure it was $0100 (which it was :). The BASIC program runs these tests a bunch of times (parameter N in the program) and calculates a few averages. The data is all stored in arrays so that it can be viewed, sorted, or whatever. The first time I ran these experiments I did so in SLOW mode and in FAST mode. In slow mode an extra 43 cycles would pop up from time to time. As several people informed me, >This has not anything to do with interrupts, it's the VIC-IIe's DMA which >halts the 8502 on any bad-line for 40-43 cycles. To avoid this jitter >switch off the screen before measuring, lda#$0b:sta$d011:lda$d012:bne*-3. >Note the loop after switching the screen off. It's important to wait until >the raster counter is out of the text area as switching off the screen >doesn't affect the current screen. After measuring you can switch on the >screen with lda#$1b:sta$d011. These extra cycles disappear in 2MHz mode, however: >Yes, switching to 2MHz forces the VIC-IIe to leave the phi1/2 DMA cycles >(almost) untouched. This is a very brutal way to get rid of the 40-43 DMA >cycles since the VIC's core doesn't know that you want him to leave these >cycles untouched until you have switched off the screen. So it still tries >to perform DMA. As this would mean a bus collision with the 8502 a >protection circuit inside the VIC shuts down its address lines, AEC and BA >on DMA times. The VIC never gets the data it wanted to display a picture, >it gets the data the 8502 has accessed - this gives are real bogus screen. >:-) As a comparison, two runs follow; the second run is in SLOW mode, with VIC turned off, and the first is in FAST mode. Both runs do 120 repetitions (N=120): FAST mode, N=120: Test 1: Overhead = 1.4 (expected=1.5) Test 2: Single register = 4.9 (expected=5) Test 3: Single reg twice = 10.14166... (expected= 9.8) Test 4: total time= 304.166... minus loop overhead= 212.166... div 37 BIT-loops = 27.166... => bit-loop repetions = 7.7619 Test 5: Two reads REG 18 = 14.36 Expected = 13.8 (twice Test 2) Test 6: One memory fetch = 115.66... Expected = 20.7 diff = 94.966... Probable waits for VDC Reg 31 fetch = 27.133... Test 7: Loop memory fill= 10755.766... Extra VDC waits= 8347.866... Avg BIT-loop repetitions = 9.3168 Test 8: Total block fill= 179.94166... => Approx VDC time spent on 256 byte block fill= 153.24166... I include the above just for the sake of comparison; my 'expected' values are very naive, and the numbers themselves are not terribly insightful. SLOW mode, VIC disabled, N=120: Test 1: Overhead = 3 (expected=3) Test 2: Single register = 10 (expected=10) Test 3: Single reg twice = 20 Test 4: total time= 554 minus loop overhead= 370 div 37 BIT-loops = 0 => bit-loop repetions = 0 Test 5: Two reads REG 18 = 28 Expected = 28 (twice Test 2) Test 6: One memory fetch = 118.766... Expected = 42 diff = 76.766... Probable waits for VDC Reg 31 fetch = 10.966... Test 7: Loop memory fill= 11614.5333 Extra VDC waits= 6747.533... Avg BIT-loop repetitions = 3.76536458 Test 8: Total block fill= 197 => Approx VDC time spent on 256 byte block fill= 143 Comments: Tests six and seven are the only tests which showed variation; all other tests stay constant throughout all trials, with one notable exception: Test #8 weirdness: _Very_ occasionally a spurious number will come up, and always only on the first trial. It is possible to duplicate: from the BASIC program, set N=5 say (so you don't have to wait forever). When the first tests are reported and the program pauses for input, hit run/stop -- the cursor should now be red. Then rerun the program, and watch the last number printed. It will usually be 200, but sometimes numbers such as 1019, 1012, 592, and 963 pop up on the first trial. If you break the program at other places, though, these numbers don't come up. This is a mystery to me. 1MHz mode with VIC turned off is the most reliable indicator of VDC's performance: Test 3 is indeed just twice Test 2, which is not surprising for register 18, since the BIT-loop is never executed. (Twice Test 2? Timid termites tremble, eliciting tempting titilations. Temperance!) Test 4 again suggests that no registers take longer to come up than others. In effect, it doesn't seem to take up any time to set up a register. Test 5 verifies our intuition that reading $D601 doesn't alter timings of subsequent operations. Test 6 is an interesting one. A typical memory fetch takes a fair amount of time! Test 4 tells us that just setting up the register doesn't take any time; it is the write to the registers, which then causes VDC to go and fetch the data, that slows everything down. This will be discussed below. Test 7 shows that memory writes are somewhat time consuming. Test 8 shows that the block-fill is a two order of magnitude improvement over Test 7 though! 143 cycles versus 11000 -- wow! Also of interest is that there were some BIT-loop repetitions; I expected them to not be necessary. Andreas Boose provides a lot of insight into some of the issues raised above: >Stephen, 2MHz mode is not so easy as you might think. There are two facts >you have to take in account: > >#1 In 2MHz mode a single raster line consists of 65 phi1 and 65 phi2 >cycles. But the 8502 doesn't get all cycles! During the last 5 phi1 cycles >of any raster line the VIC-IIe must refresh the system's DRAM and so the >8502 is switched to 1MHz on these cycles. Simply counting the cycles of >the code and dividing them by 2 is not accurate. For a serious measurement >you would have to synchronize the test loop to the VIC-IIe's refresh and >to periodically count 120 cycles @2MHz and then 5 cycles @1MHz. > >#2 In 2MHz mode accessing VIC-IIe, SID, CIAs or $DE00-$DFFF forces the >8502 back into 1MHz mode. But it is not simply done by counting these IO >accesses as 2 2MHz mode cycles: It is different whether the IO access >occurs during phi1 or phi2. If it happens on phi1 the VIC-IIe expands the >cycle into phi2 and we get 2 2MHz cycles. If it hits on phi2 the VIC-IIe >delays the IO request to phi1 and then the 1MHz mode access is performed. >So this consumes 3 2MHz cycles instead the expected 2 cycles. > >For exact measurement 1MHz mode with switched off screen might be the >best reference. But even with accurate 1MHz 8502 you will not get rid of >odd values, the VDC has its own clocking frequency derived from a separate >16MHz source which slides relatively to the 8502's system clock. Therefore >there will be always some 1/2 or 1/4 cycle jitter on any $d6xx access. > >Also for exact measurement you have to align your routine to the VDC >timing. In the moment you rely on the fact that the VDC can execute the >same command in the same time regardless on what the VDC doing on the >screen - but surely that matters. CBM engineers were 2MHz-DRAM-bandwidth- >weenies and the VDC occupies lots of its RAM bandwidth for drawing the >picture and refreshing the DRAM. So it must delay "foreign" RAM access of >the 8502 to certain timing slots like the horizontal or vertical retrace >or so. Clearly, my experiments have just scratched the surface of the VDC. We can make a few conclusions however: - In many cases the usual BIT-loops are unnecessary. The things which seem to make them necessary are the things which can tie VDC up, namely any operations which need to talk to his memory (did I mention that VDC is a 'he'?). - Some operations, such as a fill, are not only efficient but take a consistent amount of time to execute. Others, such as the memory fetch, do not. The VDC strikes me as being 'the final frontier' of the C= world, in that it is not terribly well understood and there is still much to be explored. There are two things which I think would be very nice to have, concerning VDC: first an understanding into the internal operation of the chip, and second a list of timings for various operations involving VDC, with the goal of really utilizing VDC for the graphics coprocessor that he can be. Source + 2 binaries to follow. ------------------------------------------------------------------------- * * vdctimes.s * * The purpose of this program is to gather some timing * information on the VDC chip. It simply uses CIA #1 * timer A to measure the time to perform a series of VDC * operations. A BASIC program will then statistically * assemble the data. * * Stephen Judd * 8/3/95 ORG $1300 TIMALO EQU $DD04 ;Timer A, CIA #2 TIMAHI EQU $DD05 CIACRA EQU $DD0E ;Control register A * Now some macros SETREG MAC ;Set a VDC register STX $D600 L1 BIT $D600 BPL L1 <<< ELAPSE MAC ;Calculate time elapsed SEC LDA #$FF SBC TIMALO STA ]1 LDA #$FF SBC TIMAHI STA ]1+1 <<< SEI INITTIM LDA #$FF ;Stick 65535 into STA TIMALO ;timer latch STA TIMAHI LDA #%00011001 ;Start timer LDY #%00001000 ;Stop timer CALIBRAT STA CIACRA ;Figure out overhead in NOP ;starting/stopping timer NOP NOP NOP NOP ;Elapse 10 cycles STY CIACRA LDA #$FF SEC SBC TIMALO STA TEST * First test: No OP; just load a register REGTEST LDX #18 ;Register 18 LDA #%00011001 STA CIACRA ;Start timer >>> SETREG ;A single reg STY CIACRA >>> ELAPSE,REG1 LDA #%00011001 STA CIACRA >>> SETREG ;Do it twice >>> SETREG STY CIACRA >>> ELAPSE,REG2 LDX #36 ;Now we will do them all LDA #%00011001 STA CIACRA :LOOP >>> SETREG DEX BPL :LOOP ;Sub 5*37-1 cycles from total STY CIACRA >>> ELAPSE,ALLREG READVDC ;Just a quick test to see if LDX #18 ;anything weird happens by a LDA #%00011001 ;read STA CIACRA >>> SETREG LDA $DC01 >>> SETREG LDA $DC01 STY CIACRA >>> ELAPSE,READ18 ;This should be REG2+8 WRITEVDC ;Various tests of writing to LDA #%00011001 ;the VDC STA CIACRA >>> SETREG ;Now read an actual memory STA $D601 ;location INX >>> SETREG ;Three setregs total STA $D601 ;Overhead: 6+6 cycles LDX #31 >>> SETREG STY CIACRA >>> ELAPSE,READ31 LDX #18 ;Now test memory fills LDA #00 >>> SETREG STA $D601 >>> SETREG STA $D601 LDA #%00011001 LDY #00 LDX #31 STA CIACRA LDA #$66 ;Screen code 102 :LOOP >>> SETREG STA $D601 INY BNE :LOOP ;Overhead: 2+2+5*256-1 ;(or 9*256 if sta is counted) LDY #%00001000 STY CIACRA >>> ELAPSE,FILLSLOW LDX #18 LDA #00 >>> SETREG STA $D601 INX >>> SETREG STA $D601 LDA #%00011001 LDX #31 STA CIACRA ;Here we go... LDA #$66 >>> SETREG STA $D601 LDX #24 LDA #%00000000 >>> SETREG STA $D601 LDX #30 LDA #$FF ;Total of 256 writes >>> SETREG STA $D601 ;Off it goes! LDX #18 >>> SETREG ;Hopefully this will wait until STY CIACRA ;the fill is done! LDA $D601 ;Just to make sure, check the STA MEMADDR ;last address written to INX >>> SETREG LDA $D601 STA MEMADDR+1 >>> ELAPSE,FILLFAST CLI RTS ;Whew! TEST DS 1 ;Calibration test REG1 DS 2 ;Single register test REG2 DS 2 ;Same register twice ALLREG DS 2 ;All 37 registers READ18 DS 2 ;Two reads of register 18 READ31 DS 2 ;Three reads minus 12 cyceles FILLSLOW DS 2 ;256 writes MEMADDR DS 2 ;lo/hi of memory fill (a check) FILLFAST DS 2 ;One block write of 256 chars -- begin 644 time1.0f.uu M 1P7' $ F2 BFY-0051)14Y#12XN+B( )1P" $ZR,3(P.D.R, !6' , _B8Z M1K(Q.I<@-3,R-C4LPB@U,S(V-2D@KR R,SDZCR!455).($]&1B!624, 91P$ M (\@1D%35#I&/3( H1P% (8@5#$H3BDL5#(H3BDL5#,H3BDL5#0H3BDL5#4H M3BDL5#8H3BDL5#<H3BDL5#DH3BDL058H."D ]AP* %1%4U2RT2@B,31#.2(I M.E(QLE1%4U2J,3I2,K)2,:HR.D%2LE(RJC(Z4C.R05*J,CI2-+)2,ZHR.D93 MLE(TJC(Z34&R1E.J,CI&1K)-0:HR #H=# !!5B@Q*1H:&AH:&AH:&AH:&AH: M&AH:&AH:&AH:&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: 8&AH:&AH:&AH:&AH:&AH:&AH:&AH:&AH: end begin 666 vdctest1.0d.o M !-XJ?^-!-V-!=VI&: (C0[=ZNKJZNJ,#MVI_SCM!-V-R12B$JD9C0[=C@#6 M+ #6$/N,#MTXJ?_M!-V-RA2I_^T%W8W+%*D9C0[=C@#6+ #6$/N. -8L -80 M^XP.W3BI_^T$W8W,%*G_[07=C<T4HB2I&8T.W8X UBP UA#[RA#UC [=.*G_ M[03=C<X4J?_M!=V-SQ2B$JD9C0[=C@#6+ #6$/NM =R. -8L -80^ZT!W(P. MW3BI_^T$W8W0%*G_[07=C=$4J1F-#MV. -8L -80^XT!UNB. -8L -80^XT! MUJ(?C@#6+ #6$/N,#MTXJ?_M!-V-TA2I_^T%W8W3%*(2J0". -8L -80^XT! MUHX UBP UA#[C0'6J1F@ *(?C0[=J6:. -8L -80^XT!ULC0\J (C [=.*G_ M[03=C=04J?_M!=V-U12B$JD C@#6+ #6$/N- =;HC@#6+ #6$/N- =:I&:(? MC0[=J6:. -8L -80^XT!UJ(8J0". -8L -80^XT!UJ(>J?^. -8L -80^XT! MUJ(2C@#6+ #6$/N,#MVM =:-UA3HC@#6+ #6$/NM =:-UQ0XJ?_M!-V-V!2I M_^T%W8W9%%A@ :&AH:&AH:&AH:&AH:&AH:&AH: 1&AH:&AH:&AH:&AH:&AH:&AH end /S08::$dd00::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: Software analysis and reconstructive therapy, a historical view on "cracking". by Pontus Berg Cracking was defined as the removal of copy-protection, and for this reason the product had to feature protection in the first place! Most "originals" of today do not feature any protection, but I guess what most people are after is just introlinking and crunching (correct me if I'm wrong)! I'll brief you on the whole train of thought! Cracking was much about being able to analyze someone elses code. This did not mean that you had to be able to understand exactly what each instruction did, but from the context you had to be able to tell the main purpose of a section of code. From this followed that you had to be able to know A LOT about coding. The better you knew how to code, the better was your chance of getting decent as a cracker. This was a very difficult thing, mind you. How could you detect a loader routine from the rest of the code if you didn't know 6502? The answer is that you simply couldn't! A lot of people had to learn on the fly. By digging into other people's code you got a feeling for 6502, and this experience built many a legend. If you ever saw a good cracker in action they could scan the memory with the I* command of their monitor utilities and tell what parts of the memory did what and how. :) This is all nice but what exactly went on in the mind of the cracker? Well, let's explore briefly the generalities on cracking tape games, as they are virtually non-existent in our Commodore world of 1996 and are therefore not a political hot-potato so to speak :). For tapegames, the fact that the original is on tape is the hardest part. There are transfers that copies tapedata to disk for most common tapeloaders. Doing it by hand is not something you have to do every day, but it's something you must be able to do if you want to call yourself a cracker! Mind that it's not all that easy! If you take a look at the persons who call themselves crackers today, only a very limited few are able to do this! Tapetransfer SYS 63276 This loads the header into the tapebuffer at $033c. You get the name on screen and can press space/CBM key to get back to the prompt. It won't start loading as it would have if you had written LOAD. You can now modify the tapebuffer. The five first bytes are 03 (Not relocatable file), Low/High startaddress, Low/High endaddress f.ex. 03,9f,02,3c,03 This loads a file at $029f ending at $033c. If you modify this into 03,9f,12,3c,13 You'd get it on a place where it won't start in your face (i.e. $129F)! By the way, if the start says 03,01,08,XX,YY then the game loads to basic, and you can then just type LOAD and after loading it won't autostart (unless you pressed shift+runstop), but can be saved normally to disk (unless it exceeds the limits of basic by loading over $a000). Why $129F - Well as it's easier to disassemble the code if the lowbytes are the same as the original. SYS 62828 Will load the file at the address you asked for with the pointers! (i.e. $129F) From here you are on your own, as no two tape loaders are exactly the same! I cannot say anything general about tapeloaders! If you decidper the loader and find no startaddress in it, then it might be as it's overloaded during loading. Selfmodifying loaders are frequet to prevent you from tempering with them. I usually resource the loader and place it elsewhere in memory. I can then safely transfer the files! The tapes don't usually feature any protections more that the fact that they are on tape. I seen a few games that set the timers before loading and checked them afterwards to ensure you hadn't interrupted the loading but This is rare! Last Ninja II was like this! OK, after this you have the game on disk. A one-filer shall now work 100%, no matter if it's transfered from tape or if you got it directly on disk. A onefiler 1. Depack it (if packed or crunched) 2. Remove crap that only take room and is of no use! (This is what I do GOOD that makes my cracks shorted than most of the others!) 3. Scan for trainers! (I use Action Replay first. If it finds them, I saved myself a lot of work!) 4. Install the trainers in a trainer menu & put it in memory of the game! There's almost always room for that! 5. Pack the game 6. Add the intro 7. Pack the game again (only needed if you gain something from this!) 8. Crunch Multifile games Additional work compared to the above: 1. Locate the loader and remove it 2. Analyse the code for the old loader and make yours load the correct level from the internal levelcounter of the game! 3. Replace the loader with your own one (featuring a decruncher) including the information you got while analyzing the code (see 2) 4. Crunch the levels In detail Ok you might then need some guidance in each of the steps! I guess depacking is the most important part! The depacker/decruncher starts somewhere (usually basic, at $0801), and you can then pretty easily follow the code in it. The action replay has the AWSOME feature "Set Freeze". You disassemble the code in freezemode (N.B.) and then enter a SF the the opcodes. F.ex. 4000 AD FF 4E LDA $4EFF You modify to: 4000 SF FF 4E LDA $4EFF ^^ and press return! You now get: 4000 20 D3 DF JSR $DFD3 (Or something like this) I saw QED/Triangle working, and he did a version of this using his Expert Cartridge. He did pretty much the same but manually. He installed this instead of the "SF" CLC BCC (To the CLC) After a while, when the program was surely in the infinite loop, he slammed RESTORE to enter his monitor. I have used this some times myself, when the SF was not working for some reasons (mainly as $01 was set to something that prevented SF from working). Back to depacking OK, back to the depacker! You enter this SF at the place where the decruncher jumps to the next step in the process. It might be starting the next depacker and it might be starting the game! Remember that most decrunchers are different, so you cannot take for granted that the skills from one apply on another one! Some VERY GENERAL hints: Most of the times, the startup looks something like this: SEI/CLI/NOP ;Either of these LDA #$3X ;Where X is almost always 4-7 (most often 7) STA $01 JMP $YYYY ;Where YYYY is the start address and the place to put your SF ;Set on top of the $4C, i.e. the actual jump instruction. Most decrunchers has this code in the area $0801-$0a00, but the AB cruncher and CruelCrunch has it in the very end of the file (in the last two blocks). This is where I get to be so concrete that it would be best if I had an example to show from! This will have to be all for now.. :) Pontus Berg /S09::0100h::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: - A Quick Overview of CP/M - By Mike Gordillo What is CP/M? CP/M is an operating system available for microcomputers using the Zilog Z80 Microprocessor. As an operating system, CP/M is responsible for directing your computer's resources to fit your needs. It executes software (programs) in response to commands you enter through the keyboard (console), and once executed, your programs can use CP/M to perform many tasks. For example, CP/M software can manage databases, copy files, process spreadsheets, analyze your income tax, print your forms, or play your favorite video games. CP/M 3.0+ on the Commodore 128 is simply a version of CP/M 3.0 that has been altered to better fit the particular needs of the Commodore 128 and its users. Aside from a few cosmetic differences and the ability to use the peripherals (drives, modems, etc) of the C-128 System, there is not much to distinguish general CP/M from the C-128 version. Your C-128 can execute virtually all the software widely available for CP/M machines. However, please note that CP/M software intended specifically for a certain CP/M machine may not necessarily run or run-as-intended on another CP/M machine. How do I start CP/M? Unlike the C-64 and C-128 native modes, CP/M must be started from the CP/M 3.0+ System Diskette. The standard start-up procedure requires you to have one drive set to device #8. All you do is insert the System Diskette into the drive and type BOOT from within the C-128's Basic 7.0 mode. CP/M should now "boot up" from the diskette. If this does not happen, clear memory by turning off the C-128 and the drive. Put the CP/M System Diskette in the drive and turn the C-128 and the drive back on. You should now see CP/M assume its normal diagnostic "boot up" procedure. Once that is complete, CP/M will announce itself proudly as "CP/M 3.0 on the Commodore 128" and provide you with its version date. The 28 May 87 CP/M 3.0+ version, for example, is the last and most popular official version for the C-128. Other "non-official" versions exist for the C-128, most notably the BIOS6-ZPM3-ZCCP enhancements -- but that's the subject of another article. CP/M is a command-line interface! CP/M is a command-line interface. This means that your instructions to CP/M must be entered as commands to the system from the keyboard (as opposed to mouse control in popular Windowing environments like GEOS). You enter commands via the A>, B>, C>, D>, E>, or M> prompts. These prompts tell you which disk drive (device) you are currently using as well as letting you know that CP/M is ready to receive your orders as soon as possible. For example, if I see the A> prompt, I am using device 8, but if I see the B> prompt, I am using device 9, and so on. I can change between devices by simply entering the appropriate letter and a colon, as follows: A> B: (Changes from Drive A to Drive B) B> C: (Changes from Drive B to Drive C) C> A: (Changes from Drive C to Drive A) A> (Back to Drive A) The E> and M> prompts are special cases. Drive E is not a real drive/device, rather, it is a Virtual Drive used for copying purposes when only a single real drive is available. Drive M is not a physical drive at all. It is assigned to the Ram Expansion Unit as a RAM drive. How is the CP/M Command Structure organized? CP/M has six built-in commands: DIR, DIRSYS, ERASE, RENAME, TYPE, USER. These commands are part of your CCP.COM file (Command Console Processor) and they make it easy for us to perform basic level housekeeping. DIR allows us to call up a disk's directory of files. DIRSYS does the same thing except it shows us only files tagged with a system-flag (eg. hidden files). ERASE and RENAME allow us to erase and rename files while TYPE is used to display text files on the screen. USER lets us switch between different user sections on a diskette. A CP/M diskette can have 16 user sections (0 to 15) in which files placed in one section are logically separate from files in different sections. User sections are CP/M's way of allowing for better organization of files through partitioning. Any other "commands" you might run into are known as transient-utility commands. Transient-utility commands are simply executable programs that reside on a diskette instead of being built into CP/M itself. They are usually identifiable on the diskette by virtue of their .COM extensions. For example, if I call up a disk directory using the DIR command, as follows: A> DIR BASIC.COM DISC=OV.ERY ISAGREAT.MAG SORT.COM TERM.COM This tells me that on Drive A (device 8), the files basic.COM, sort.COM, and term.COM are transient-utility command files. Notice that there is no DIR.COM available to call up a directory because the DIR command is a built-in command. Does my System Diskette have Transient-Utility (T-U) Commands? Yes, Commodore put several useful T-U commands into the CP/M 3.0+ System Diskette. You should have the following in order to begin to effectively use CP/M on the C-128, as follows: A> DIR C1571.COM CONF.COM FORMAT.COM KEYFIG.COM HELP.COM PIP.COM C1571.COM is a neat T-Utility that will double your 1571's SAVE speed by disabling the automatic verification involving the saving of a file to the diskette, as follows: A> C1571 [a (Disables auto-verify on Drive A - Device 8) CONF.COM is a GREAT T-Utility that permits us to change many system-default parameters like baud rate, screen colors, printer assignments, etc. When I first started, I was very very frustrated at what appeared to be immutable artifacts in the operation of the C-128's CP/M. In other words, I could not CONFigure the system as I wanted. This utility changed all of that. For example, I used to have a 4040 dual drive which I hooked up to the system intent on using it under CP/M. I found to my dismay that I could only access one out of the two drives huddled in the 4040 bay. CP/M had no way of knowing that the single device it saw (the 4040) had more than one drive! Along came CONF.COM to the rescue! I simply typed up CONF DRVB = 8-1 and voila, the second drive in the 4040 became Drive B (which would normally be device 9). CONF is also great at modifying CP/M itself when needed. I recall one time when I was using another T-Utility that had problems with the Ram Expansion Unit under its normal device assignment. In other words, the utility I was using could not handle a drive M. I used CONF's POKE ability to change the drive M assignment of the R.E.U. into a drive B assignment, as follows: A> CONF POKE fbd3 = 96fb (Reassigns the REU from drive M to B) FORMAT.COM allows you to format your diskettes in a variety of CP/M formats, all of which can be read by the C-128's CP/M. KEYFIG.COM is a T-Utility which allows you to redefine ALL keys on the C-128 except the SHIFT and SHIFT-LOCK keys, the 40/80 DISPLAY key, the CONTROL key, and the COMMODORE key. You can assign keys to have up to four different values/functions to suit your needs. HELP.COM is a good manual of CP/M commands and command syntax. It serves as an index of commands and tells you how to properly use them. PIP.COM is the standard file-copy T-Utility. I use it routinely to create backups of my files as well as creating new system diskettes in cooperation with the FORMAT.COM utility, as follows: A> FORMAT (Use the Format utility to format drive B) A> PIP B:=A:CPM+.SYS (Copies the CP/M system file from Drive A to B) A> PIP B:=A:CCP.COM (Copies the CCP.COM file from Drive A to B) Is that all I can do with CP/M? No, Commodore packaged CP/M for the C-128 with just the basic entry level material necessary to begin to use CP/M effectively. CP/M can do much more but like all things concerning computers, you need a good measure of software to make the best use of what you've got. There is nothing harder for the novice than to accumulate enough software to increase his and his system's proficiency. I recall many times thinking how useless CP/M was to me. After all, I could do so much more in C-64 and C-128 modes! However, once my software base had risen beyond what the System Diskette offered, my analysis of CP/M changed from useless to useful. In other words, CP/M productivity is in software? CP/M's strength lies in the large software base available for it. CP/M is the first operating system to showcase the functionality of microcomputer database and spreadsheet applications on a large scale. I regularly keep large databases and spreadsheets on the C-128's CP/M and then use other CP/M productivity programs to transfer them to and from other types of systems. Furthermore, some of the best software compression programs and computer languages are only available for the C-128 whilst in CP/M mode. With over ten thousand software titles to choose from, there is nothing you can't do! Where can I get CP/M software? You can get good CP/M public domain software from CP/M User's Groups throughout the United States and Canada. There is also substantial CP/M support in Japan and in Europe. Commercial software is readily available from appropriate vendors (usually listed in user group publications). The most substantial concentrated source of CP/M software is undeniably the Internet. The Internet is a collection of various computer networks linked together throughout the world. There are thousands of megabytes of CP/M programs (from 1976-1993) available there. A few popular 'CP/M sites' are as follows; oak.oakland.edu /pub/cpm ccnga.uwaterloo.ca /pub/cbm/os/cpm And pray tell, how do I transfer CP/M software into the C-128? Well, there are a number of ways. The most efficient way is to copy files from one CP/M disk onto another. The C-128's CP/M can read the CP/M diskettes from Epson, IBM, Kaypro, and Osborne CP/M machines. You can also use terminal programs if your CP/M's version-date is later than 4 DEC 85 (ie., earlier versions lacked access to the modem port) in order to transfer material via the phone lines. The most inconvenient way, however, is to use transfer programs (from either CP/M or other modes) to read material onto your CP/M diskettes. Come on, I'm looking for something simple. Is there a CP/M CD-ROM? Sure is! A few people out there decided to put several megabytes of CP/M software on CD-ROM (!) and just a few months ago, someone wrote a CD-ROM reader for C128 users with a CMD Hard Drive (SCSI port)! The "Walnut Creek" CD-ROM and "CD-ROM Commander" are two very exciting products for CP/M 128 users, as these items allow access to hundreds of megabytes of CP/M software. (Please consult the internet newsgroup 'comp.sys.cbm' for more details). Final thoughts... CP/M complements the C-64 and C-128 modes very well. I feel that in the productivity area, the C-128 would be very deficient otherwise. I have yet to see productivity packages in the native modes that are as efficient as the ones I use under CP/M. The structure of the CP/M operating system is also more amenable to file transfer and file management. My Ram Expansion Unit has never been as easy to work with in the C-64 and C-128 native modes as it has been under CP/M. CP/M may seen complicated at first but it is actually quite easy to understand. Once you've mastered it, you may never go back. ----- Mike Gordillo is an expert in CP/M and Z80 programming as well as a devout Commodore fanatic over the past twelve years. He may be reached on the internet as s0621126@dominic.barry.edu for general comments or questions. /S10::0100h::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: The BIOS-R62a/ZPM3/ZCCP Commodore 128 CP/M 3.0+ Upgrade Package and a bunch load of utilities! by Mike Gordillo Preamble This package was an in-house project for mostly myself and a few of my friends. I released this last year to the general internet community, and as far as I know, all software therein is public domain. What to do: FTP to ccnga.uwaterloo.ca Directory is /pub/cbm/os/cpm Download (remember to set binary flags) the following: zpm-user0.zip zpm-user1.zip upgrade-user0.zip Unzip them on your unix/vms/dos box and put them on a C128 CP/M 1571 or 1581 boot disk (eg., BBR 4.1) in the following manner: All files from zpm-user0.zip go to USER 0 on your CP/M boot disk. All files from zpm-user1.zip go to USER 1 on your CP/M boot disk. IF you have any of these: Swiftlink cartridge, an ASCII printer, absolutely NO ram expansion whatsoever, a 1581 drive, you will probably need to take a look at upgrade-user0.zip. It contains replacement files that satisfy the hardware I just listed. IF so, you MUST use the replacement files to insure proper operation. All replacement files MUST go to USER 0 on your CP/M boot disk. Please READ the read.lis file in the upgrade-user0.zip archive for more details. Note: If you unzip these archives on a VMS system, chances are that CPM+.SYS will be renamed to CPM.SYS. You MUST make sure that you rename it back to CPM+.SYS on your CP/M boot disk, otherwise CP/M will not boot. For more information on BIOSR6 and ZPM3/ZCCP enhancements please consult Randy Winchester's CP/M article in the online magazine, C= Hacking issue #5. Abstract This package is provided as a courtesy to C128 users. Meaningful CP/M system-generation utilities are not provided with the C128 CP/M System diskette. Most C128 users would therefore have difficulty in assembling a package like this. Also, the lack of easily available sources for CP/M software brings its own share of hardships. The utilities included are meant to provide new tools for the C128 user and maximize the features of BIOS-R62a, ZPM3, and ZCCP. Filelist zpm-user0.zip: Length Date Time Name ("^" ==> case ------ ---- ---- ---- conversion) 512 01-03-95 11:41 autotog.com 4480 01-03-95 11:41 b5-driv3.com 512 01-03-95 11:41 bye.com 128 01-03-95 11:41 c128-xgr.z3t 1024 01-03-95 11:41 c1571.com 3200 01-03-95 11:41 ccp.com 24576 01-03-95 11:41 cpm+.sys 1152 01-03-95 11:41 echo.com 3840 01-03-95 11:41 format.com 16384 01-03-95 11:41 format22.com 640 01-03-95 11:41 format81.com 3328 01-03-95 11:41 if.com 3456 01-03-95 11:41 loadseg.com 5376 01-03-95 11:41 mkdir32.com 256 01-03-95 11:41 names.ndr 17536 01-03-95 11:41 qterm.com 17408 01-03-95 11:41 rdcbm.com 8192 01-03-95 11:41 salias.com 3456 01-03-95 11:41 setpth10.com 1024 01-03-95 11:41 startzpm.com 8192 01-03-95 11:41 superzap.com 24320 01-03-95 11:41 trans128.com 7424 01-03-95 11:41 v.com 15488 01-03-95 11:41 vde.com 2816 01-03-95 11:41 verror.com 15744 01-03-95 11:41 vlu.com 2048 01-03-95 11:41 zdt12.cfg 7936 01-03-95 11:41 zdt12.com 15232 01-03-95 11:41 zfiler.com 1536 01-03-95 11:41 zinstal.zpm ------ ------- 217216 30 zpm-user1.zip: Length Date Time Name ("^" ==> case ------ ---- ---- ---- conversion) 128 01-03-95 14:47 clrhist.com 3328 01-03-95 14:47 conf.com 5996 01-03-95 14:47 conf.hlp 3072 01-03-95 14:49 copy.com 3200 01-03-95 14:49 date.com 2816 01-03-95 14:50 del.com 3712 01-03-95 14:51 diff.com 3712 01-03-95 14:51 dir.com 1280 01-03-95 14:52 dirnames.com 2944 01-03-95 14:52 diskinfo.com 1664 01-03-95 14:53 image.com 6912 01-03-95 14:53 lt.com 12928 01-03-95 14:55 pmext.com 3712 01-03-95 14:58 ren.com 1792 01-03-95 14:58 rsxdir.com 4736 01-03-95 14:59 unarc.com 12288 01-03-95 15:00 unarj.com 1408 01-03-95 15:02 undel.com 3456 01-03-95 15:03 unzip.com ------ ------- 79084 19 upgrade-user0.zip : Length Date Time Name ("^" ==> case ------ ---- ---- ---- conversion) 24576 01-03-95 15:41 cpm+.sys 3840 01-03-95 15:40 f1581.com 256 01-03-95 15:41 names.ndr 17536 01-03-95 15:46 qtermsl.com 732 01-03-95 14:08 read.lis 1024 01-03-95 15:41 startzpm.com ------ ------- 47964 6 Condensed History BIOS-R62a- Default System Baud Rate set at 134. Warning: Term programs will modify this. Higher Baud = Faster Keyboard Scanning = Slow CP/M Re-implemented support for PETSCII printers (code from BIOS R4) LST Settings : PRT1=Dev #4, PRT2=Dev #5, Secondary Address = 7 CONF utility's PRT assignment options will not work because of changes made back in BIOS R4. ASCII printers are available with the ASC-PRT implementation of BIOS-R62a (included..see BIOS R5). -CPM+.SYS for ASCII printers is included in upgrade-user0.zip BIOS-R62 - Default System Baud Rate set at 75 (not enough Keyboard Scanning). Added support : C=1581 Official Format! (F1581.COM will allow you to make/create 1581 boot disks... included in upgrade-user0.zip). MAXI 71 and GP 1581 Format supported (see BIOS R5). ASCII printers still default. -Randy Winchester BIOS R5 - Added support for new hardware: Quick Brown Box (E:), Drive D: Added new definitions to the disk-parameter-table. Maxi 1571, GP 1581 formats. Use format22 & format81 with these MFM types. ASCII printers now default. PETSCII tables not supported. LST Settings : PRT-D4=Dev #4, PRT-D5=Dev #5, Secondary Address = 5 -Randy Winchester BIOS R4 - Removed the 40 column routines, the virtual drive, and Drive D:. Removed Printer Buffer (Lord knows why!?) Added a screen dump feature. Fixed several BIOS errors as well. -James Waltrip IV ZPM3 BDOS- (see below) -Simeon Cran ZCCP CCP - (see below) -Simeon Cran **DISCLAIMER** ** **You are free to distribute this package with the following conditions; ** ** A) This package cannot be sold. A copying/handling ** fee of no more than $5 1993 US dollars is allowed. ** ** B) This package shall remain whole. No item may be ** distributed apart from the rest of the package. ** There is a degree of hidden cross-dependency between ** some items. Split them apart and you may get ** unpredictable results! ** **This package is NOT under any warranty or guarantee of ANY kind! Description BIOS Upgrade - The C128 28 May 87 CP/M 3.0+ Version BIOS was reworked to remove useless code (40col screen and Virtual Drive...not really useless to some of us? Argghhhh! :) and to correct a few CP/M 3.0 BIOS errors while adding a screen dump feature (ALT key is used as a toggle). End Result = Faster, more "peppy" CP/M 3.0+ operation. BDOS Upgrade - This is the ZPM'ing of CPM! Think of this as a way-overdue correction for an anachronism. Much of the original BDOS is written in slower Intel 8080 code. The ZPM3 BDOS upgrade rewrites things in faster, richer Zilog Z80 code while adding some goodies (eg., command history buffer, enhanced command line editing, automatic command prompting) and correcting some CP/M 3.0+ BDOS errors. ZCCP Upgrade - The last nail in the coffin. The original CCP.COM is replaced by a more flexible beast. Neat things are now at your beck and call. ZCCP features : ZCPR 3.3 Compatibility (see below) -Does not support FCP but supports flow control internally with an IF.COM utility present. -RCP is not implemented (That's what REUs are for :) -Cannot load ZCPR 3.4 "type 4" programs -Cannot re-execute loaded programs sans re-loading Z3T Termcap (ZCPR 3.3 graphics support) Named User Groups/Directories Command Search Path System Environment Block Flow control processing for batch files Extended Command Processor for batch files Multiple commands on one line Superior error handling Up to 4 Shell stack levels may be defined Direct loading of .RSX files without GENCOM (LOADSEG) Put these all together and you have the ultimate CP/M system for your C128. Compatibility BIOS R62a - 99.00% Compatible with stock CP/M 3.0+ C128 system. -A problem concerning printing is listed in the Condensed History section. (Note: Programs that call the 40col screen will see a NULL40 label -i.e., They will run but they won't be able to do anything in 40cols) ZPM3 BDOS - Fully 100% compatible with stock CP/M 3.0+ BDOS segments. (Note: Some rare programs *demand* the CP/M 2.2 BDOS..yuck!) ZCCP CCP - ZCPR 3.3 compatibility as seen in the Description section. Environment info is larger than before. Slightly more TPA is used or some additional high memory is being toyed with. For example, I shortened TRANS128's buffer and that did the trick. (Note: Some of Steve Goldsmith's C128 specific programs will will crash with the ZCCP.RSX in operation). Additional Notes -Look at the ALIAS (included) batch file (type SALIAS STARTZPM on the command-line). Notice how it optimizes the system for REU use. You may change this as long as you keep the following in mind : ZCCP *requires* : LOADSEG commands for NAMES.NDR and *.Z3T Termcaps. At least *ONE* SETPTH drive search/path entry. Quick Summary of STARTZPM batch file (included): 1) Loads up Directory names. 2) Loads up Directory paths. ($$$$ = Current Drive/User DIR) 3) Executes a few .COM files. 4) Copies all Command Utilities to M1: (COMMAND Directory). -Utilities copied over to the REU (as seen in STARTZPM) are general purpose utilities meant to replace the built-in commands of the standard CCP.COM. With the excellent path setups in ZCCP, utilities in the speedy REU become transparent. (Note: If you have NO ram expansion (eg., a drive M:) you NEED to take a look at upgrade-user0.zip, as mentioned earlier). -Multiple commands on the command line must be separated by semi-colons. (Note: Semi-Colons are used in CP/M 3.0+ to append file passwords. Use the SET.COM utility (SET [DEFAULT=PASSWORD]) to set a password which can be used without your intervention on every file you access. In any case, you can assign passwords to user groups (under ZCCP) with the mkdir32 utility, which is simpler than dealing with CP/M 3.0+ SET.COM password assignment schemes.) -The following keys have been already configured to work best with ZCCP: They are user-definable with KEYFIG or LOAD/SAVEKEY utils (not included). -CRSR-UP/DOWN - CRSR-LEFT/RIGHT KEYS UP = CTRL-E DOWN = CTRL-X LEFT = CTRL-S RIGHT = CTRL-D CTRL-E and CTRL-W = Forward and Backtrack through Command-History Buffer. CTRL-X = Delete everything to the left of the cursor. -ARROW KEYS (at the top of the keyboard) UP = CTRL-E DOWN = CTRL-X LEFT = CTRL-A RIGHT = CTRL-F CTRL-A and CTRL-F = Autotab left and right. -CLR/HOME = CTRL-H (BackSpace) INST/DEL = CP/M RUBOUT KEY -ZCCP does not support printable control characters (eg., ESC, CTRL-Z) on the command line. In order to change screen display characteristics use the CONF.COM utility (included) instead of, for example, using ^[^[^[<screen/char color code>. Also, although CTRL-Z will not clear the screen anymore, you can use the built-in CLS command instead. (Note: You can still use printing codes in programs.) -Consult the C128 system manual for the full list of ADM-3A to C-128 key assignments and sequences. -- For general comments on this article, feel free to contact Mike Gordillo at s0621126@dominic.barry.edu :::::::::::d:i:s:C=:o:v:e:r:y:::::::::::::::::i:s:s:u:e::1:::::::::::::::::::: \H01::::::::::::::::::::::::::H A R D W A R E::::::::::::::::::::::::::::::::: :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: BEYOND STEREO The POWER-SID Model 2400 : It May Not be THX, but it's Taking the Commodore One Dimension Closer A preview by Shaun Halstead A few months back, as I was working on some music using Robert Stoelle's Stereo Sid Editor, I got to thinking about how many voices I would need for this and that, and I realized that I was going to need more than twice as many as I had available. I called up Nate Dannenberg (a.k.a. _Tron_) to see if he knew of any way to get more voices; he said that the only feasible way was to add more chips, of course. So, being the annoying pest I am, I got to thinking of ways to do just that, and the concept of surround sid board was born. Carrying eight, count 'em, eight, sid chips, at three voices each, combined to give four main channels (front and rear, with left and right on each), working in tandem with the computers stock sid chip, which acts as a center channel, the Power-Sid is carrying the Commodore into a new dimension of sound. Well, to make a long story short, between the two of us, we developed the basic components, layout, and operation, including addressing options. Currently, the board is designed to mount vertically, similiar to the Super-CPU developed by CMD. This was done mainly to conserve space behind computer, but has another advantage: by mounting the card vertically, a pass-through port is easily installed (it was a given from the start that a pass-through be available). Until recently, we were having considerable difficulty in solving the pass-through mounting problem (i.e. where to put it), until Nate found a way around it, based on the Super-CPU. The actual construction will not be easy, by any means, but it should be quite strong, durable, and nice in overall appearance. The model described here (model 2400) will measure roughly 4-by-4 inches, contain eight sid chips, two stereo jacks, addressing and support circuitry. The support circuitry includes an enable/disable switch, activity LED, an addressing switch, selectable between $DE00 and $DF00, and a pair of LEDs indicating the current address, a two/four-channel selection switch and dual-color LED to indicate the current mode. The only difference between the model 2400, described here, and the model 1200 is that the 1200 carries only 4 chips, and offers fewer voices per channel, but is none the less, fully featured. Currently, software is being developed by Nate and I, and we are working on a port to support his up and coming digital output card for the expansion port. Look for an extensive follow up in this journal when the board enters the prototyping stage. -- The author, Shaun Halstead, can be reached at tesla@onyx.southwind.net, and Nate Dannenberg can be reached at tron@wichita.fn.net, or catch them on IRC channel #C-64, as _Tesla_ and _Tron_, respectively). \H02:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: * The 8 bit Modplay 128 Board * by -- Nate Dannenberg -- Have you ever listened to Digital sound on your Commodore and wondered where all the noise is coming from? Ever tried messing with Pulse Width Modulation, only to realize that it just doesn't have the clarity and impact you need for a particular project? Want to make your Commodore sound better than it ever has using 8 bit sound? Well, now's your chance... Included here is the procedure for building a two channel 8 bit DAC circuit to plug into a Commodore 64 or 128 (works in both modes), which outputs in stereo two-channel sound. Actually, the 8 bit DAC chip we will be installing does four tracks, which are mixed using four 1K resistors into two channel stereo sound with a total resolution of 9 bits per channel, or 8 bits per track. This circuit is based around the Maxim Electronics MAX505xCNG chip, where x refers to a number of chip-packaging options provided by Maxim. The recomended chips are the MAX505 A- or B- CNG (24 DIP Narrow Plastic package). This chip contains four 8 bit DAC circuits, each with 5 volt rail-to-rail swing, internally buffered and coupled with precision unity-gain followers that slew at 1V/uS (If you can understand all that jazz, you're a better man than I). Each DAC accepts it's own reference voltage input (not to exceed the power supply voltage, which is 5 volts in this project), and each generates it's own buffered output. Originally, this circuit was designed to work on a Commodore 128 with the upcoming MODplay 128 software, also one of my projects. Using this software, and outputting to this circuit, I get about 8.2 Khz out of it in four track, two channel, 8 bit-per-track (9 bit total) stereo. Since this circuit is relatively simple to access, it can be used in many other applications, from audio output from a simple RAW or WAVE player type of program, to more complex applications requiring a controllable analog signal. With the right timing, you could probably use it to control the sync signals to an RGBI monitor! Anyways, let's get on with it. First we need to get the legal stuff out of the way... Disclaimer: I take no responsibility for any loss or damages occuring from the use of this device, or from the instructions and proceedures described in this text. Any damage resulting from the use of this text is purely the responsibility of the builder/user. [Ed. Note : The editor nor disC=overy magazine is not reponsible for any loss or damages occuring from the use of this device, or from the instructions and procedures described in this text.] Here is the parts list: 1) Maxim Electronics MAX505ACNG (or -BCNG) DAC chip. 1) 12/24 .156" Female edge connector. 1) 1/8" stereo (three-conductor) miniplug. 4) 1K, 1/8 or 1/4 Watt resistors of either 5% or 10% tolerance. * a bit of thin wire (preferrably wire-wrapping-wire) * a bit of heavier wire (about 20 guage) * solder (silver-based is preferred, but lead based works too) * A low-wattage soldering iron (mine is only 20 watts) Optionally, you may build this onto a printed circuit board, which will require a piece of double-sided copper clad board about 1" by 2.5", a resist-ink pen or dry transfers, and some Ferric Chloride (PCB etchant). [Ed. Note : Maxim Electronics : 1-800-998-8800 ; Radio Shack has the jack and they do carry a .156" connector but with 22/44 leads. I had to cut it to 12/24 in order to match the userport. Also, it must be noted that soldering to a socket is less of a risk than soldering to the chip itself.] I built the prototype by mounting the chip directly to two pins of the user port connector. Since the design is so simple, I will describe the procedure for building the circuit using the prototype method, without using a printed circuit board. Here we go: 1) Turn the edge connector to point the solderable pins towards you. Cut off pin 1, pins 3-7, and pins 10-12. These are on the TOP row, with pin 1 being on the left end when the connector is held as above. left 1 2 3 4 5 6 7 8 9 10 11 12 right 2) Cut off pin A and B. This is on the bottom row, second from the left. Remember that as with most Edge connectors, there are no pins G or I. Instead the pins are lettered like this: left A B C D E F H J K L M N right 3) Align pins 12 and 13 of the Max chip, with pins H and F (respectively) on the User Port plug. Make sure the chip and plug are right side up. now bend the chip's pins around and under the plug's pins, and solder. 4) Using a series of short wires, connect the following using ordinary point-to-point soldering. Chip Plug 16 C 15 D 14 E 13 F (already soldered) 12 H (already soldered) 11 J 10 K 9 L 5) Using more point to point soldering, connect the following: Chip Plug 22 two 19 M 18 nine 17 eight 8 N 6) Turn the device upside down and make the following connections on the back side of the chip. Be sure not to confuse your pin-layout! Again, plain old point-to-point soldering is the key. Pin 20 to pin 21, Pin 21 to Pin 22, Pin 22 to Pin 4, Pin 4 to Pin 5. Pin 8 to Pin 7, Pin 7 to Pin 6, and Pin 6 to pin 3. 7) Turn the circuit back right-side-up again, and connect one end of each of the four 1K resistors, to the Max chip, pins 1, 2, 23, and 24. 8) Now Take the resistors connected to pins 2 and 23, and connect the free ends together. Connect this point to the Miniplug TIP tab. 9) Take the resistors connected to pins 1 and 24, and connect the free ends together. Connect this point to the Miniplug SLEEVE tab. 10) Connect the Max chip's Pin 6 to the Miniplug GROUND tab. 11) Take a piece of the heavier wire, fashion a simple pull-handle by running a wide loop of it between the holes at each end of the plug. For strength I recommend soldering the ends together. [Ed. Note : Please observe : Pins 1 and 24 are used as RIGHT output and pins 2 and 23 are used as LEFT output. On a 1/8" stereo miniplug jack from Radio Shack (#274-249a), you will see three prongs. The prong closest to the opening of the jack is the GROUND, and for reference's sake we will consider that this prong is at the base of the jack. The other two prongs are on the back of the jack and are aligned one 'above' the other. The one that is on the base is the SLEEVE and the one above it is the TIP.] Your 4 channel DAC circuit is now ready to use. To test the circuit, you can run this short BASIC program on a Commodore 64, or a Commodore 128 in 64 or 128 mode. In 128 80-column mode, you may want to type 'fast' and hit return before running, to make the test higher pitched and a bit more accurate. 10 u=56577: poke u+1,63: poke u+2,100: input "channel (1-4)";c$: c = val(c$) 20 if c<1 or c>4 then 10 30 if c=1 then ch=248 40 if c=2 then ch=252 50 if c=3 then ch=240 60 if c=4 then ch=244 70 poke u-1, ch 80 for x=0 to 255: pokeu,x: next 90 get a$: if a$="" then80 100 goto 10 What you should hear coming out from the desired channel is a sawtooth wave, simialar to the sawtooth save the SID chip produces, but of course a tad * distorted * since this is in BASIC. Press any key to stop the test. In machine language, these instructions should produce a similar test.. POKE 250,speed: POKE 251, chan_index: SYS 3072.. speed will control the pitch of the sound. Chan_index is 248 for channel 1, 252 for channel 2, 240 for channel 3, and 244 for channel 4. Press the spacebar to stop the test. Hexadecimal Decimal equivalent START * = $0C00; 3072 SEI LDA #$3F; 63 STA $DD02; 56578 LDA #$FF; 255 STA $DD03; 56579 LDA $00FB; 251 STA $DD00; 56576 LDY #$00; 0 LOOP STY $DD01; 56577 LDX $00FA; 250 DELAY DEX BNE DELAY INY LDA $DC01; 56321 CMP #$EF; 239 BNE LOOP CLI RTS Accessing the board via your own software is very simple.. You need to use Machine code if you are planning to access more than one channel of the board. If you plan to use a single channel, just POKE 56576,248 and run your player routine. This will select channel 1 (left side) to send your output through. In machine code, you use a single LDA:STA pair to select the channel, and just stuff your data into $DD01, something like the above. The hardware will take care of the output, and will see to it that the byte you send out is properly clocked and that it goes to the correct channel. The User port contains a total of 10 easily programmable data lines (the others require more complicated methods to use). These lines are broken into two sets. The first set, the data bus as I like to call it, consists of the 8 lines than make up the User Port's PB0-7 area. These bits are all present at location $DD01. The data direction regiter for this location is at $DD03. A 1 bit there means an input, while a 0 bit signifies an output. The other set consits of two bits that sit at location $DD00, these bits are PA2 and PA3, also known as RS232 data out, and Serial ATN in, respectively. These two bits serve as a mini-address bus, and are used to select the correct channel to comminicate with. Since the hardware inverts the output of PA3, it was necessary to re-invert this bit in software, which is why the channels are being selected with such odd numbers. Also, since location $DD00 controls the 16K memory bank seen by the VIC-II chip, you must keep the VIC pointing to bank 0 (by setting bits 6 and 7) if you want to keep the VIC 40 column display in proper order. This explains why all of these numbes are 240 (#$C0) or higher. Most DAC chips require some sort of clock signal to pass data into the chip. The MAX505 is no exception. In order to lessen the time needed by the computer, we wil ise the User Port's PC2 line, a low active clock line that fires every time a byte is sent or received via the PB0-7 lines. This line was intended to drive the /FLAG input of another C64, or other hardware device, however, it also serves as a useful clock signal for our chip. The MAX505 requires you to select the channel first, write the data byte to it's data bus, and then pull the /WR line low for a brief moment. Thus we write our code like this, to do these actions in proper order: LDA #$Cn ; n is 8, C, 0, or 4, for Channels 1, 2, 3, and 4 ; respectively. Channels 1 and 4 are wired for left ; output, while 2 and 3 are wired for right. STA $DD00 ; This actually sets the channel number. LDA your_data_here ; Use whatever method you need to get the next byte of ; Sample data into the accumulator. STA $DD01 ; Send the byte to the User port, automatically ; firing PC2. That's pretty much it! The MAX505 chip is designed to handle in excess of 100,000 samples per second, per channel. In fact, according to the specs, it can go as high as 1 million samples per second, per channel. Below you will find a pinout diagram for the MAX505 chip, to aid in performing the steps given above for assembling this circuit. This is a duplicate of the diagram found on page 1 of the spec booklet that comes with the MAX505 family. TOP VIEW ___ ___ Ü. À-' Ü VOut B [ 1Ü Ü24] VOut C VOut A [ 2Ü Ü23] VOut D VSS [ 3Ü Ü22] VDD VRef B [ 4Ü MAXIM Ü21] VRef C VRef A [ 5Ü MAX 505 Ü20] VRef D AGND [ 6Ü Ü19] A0 DGND [ 7Ü Ü18] A1 /LDAC [ 8Ü Ü17] /WR (MSB) D7 [ 9Ü Ü16] D0 (LSB) D6 [10Ü Ü15] D1 D5 [11Ü Ü14] D2 D4 [12Ü Ü13] D3 Ü_________Ü DIP/SO/SSOP Have fun with this circuit! I built mine in about 30 minutes, and it has become my default playback device for my Modplayer. I will also write in full support for this device in Sound Studio 4.0 (commercial), as well as any other digital sound programs I happen to write later. The MAX505 chip will also be used in another board I am designing, called the E-8 (Enhanced-8) board. This board will use two MAX505's to provide an effective output resolution of 16 bits per channel. The board will also feature a pair of National Semiconductor ADC0820BCN chips, for sampling in 8 bit two channel stereo (Unless I find a better chip to use). No release date or pricing has been set for this board as of yet. -- For more information on this or general commentary, you may reach Nate at the following addresses: Dannenberg Concepts, Ltd. "Bringing your Commodore 128 one step closer!" Email: tron@wichita.fn.net Phone: (316) 777-0248 Snail: Dannenberg Concepts, Ltd. c/o Mr. Nate Dannenberg 306 S. 1st Ave Mulvane, KS 67110 FidoNet: 1:291/25 Groups: CBM, CBM-128, and CBM-GEOS Usenet: comp.sys.cbm, alt.binaries.sounds.mods \H03:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: Upgrading your C128's VDC memory to 64K by Mike Gordillo This will be a quick "how-to" on upgrading your VDC memory from 16K to 64K. Those of you who have C128-DCR's should already have this upgrade from the factory, but those who own original flat C128's or original C128D's may wish to undertake the following VDC ram expansion. First off, the last time I checked, you needed two 64x4 DRAM's and the prices for such ram chips were as follows: LA Trade - 1-800-433-3726 64x4 DRAM - 80 nanoseconds = $3.75 Nevada Computer - 1-800-982-2925 64x4 DRAM - 100 nanoseconds = $2.45 COMP-USA !!! 1-800-COMP-USA 64x4 DRAM - 100 nanoseconds = $1.50 ! ! ! ! The instructions for installing them into your machine quite simple. Just open up your C-128, remove the shielding, keyboard connector, etc. You should now see a metallic box around the center of the C-128's motherboard. Remove the lid on that box with the keyboard facing you, revealing the following: >>>>>>> Back of C-128 <<<<<<< ---**--- ------ / - - - LC - / - 8 - - - / - 5 - - - / VDC - 6 - --**-- / VIC Chip Chip - 3 - / Circuitry - - ------ ------ / to the right - V - - 16 - - 16 - / - D - - x - - x - / - C - - 4 - - 4 - / - - - - - - / <---These two 16x4 DRAM's - - -DRAM- -DRAM- / are to be replaced with -------- --**-- --**-- / 64x4 DRAM's. ** = designates the directional notch on the chip >>>>>>> Front of C-128 <<<<<<< In your machine you will see two 16 by 4 DRAM chips (16 kbytes total). Your task is to replace them with 64 by 4 DRAM chips (64 kbytes total) that you can get cheaply at CompUsa for example :)...no minimum order.. :)). The hard part is now at hand. The 16 by 4 chips are not on stacks! This means you have to unsolder them. If you have never played with a soldering iron before this is going to be a BIG pain in the rear and you may damage your C-128 in the process. (See, isn't it neat to find out for FREE instead o paying Slick Wile up front?) If you have some experience with a soldering iron, remove the C-128's mother board from the case, flip it over, and find the pins for the 16 by 4 DRAMs on the back. Unsolder them and remove the chips. Solder the 64 by 4 DRAM chips in the same exact spots as the 16 by 4 chips, remembering to keep the notch on the 64 by 4 chip facing the notch on the original 16 by 4 chip. Solder in stacks/sockets first, and then plug in the 64 by 4 chips, because it is safer to solder in the sockets AND if a memory chip goes bad, you won't have to unsolder again (just pop out the ram chip from its socket.) 18-Pin Stacks/Sockets are 50 cents each at Radio Shack. DISCLAIMER : You undertake this procedure at your own risk. If you destroy your machine, its your cross to bear. I did this to a C-128 last Sunday. It took 2 hours with the simple soldering tools I have. It may take you less time or more time depending on your equipment and experience. DRAMs are a pain to remove in any case. I sped up removal by just cutting the legs on the original 16 by 4 chips with a manicurist's scissors. Definitions : 16 by 4 (16 x 4) = 8 Kbytes of memory 64 by 4 (64 x 4) = 32 Kbytes of memory DRAM = Dynamic Random Access Memory 8563 VDC = The C-128's 80 column video chip. LC = Logic chip next to VDC, more specifically, its a 74LS244. Specific labelling on the original 16 by 4 DRAM: You should either see TMS4416 on them or MB81416. They are usually 120 nanosecond chips, so you must buy 64 by 4 chips that are at least 120 nano- second variety. I chose 100 nanoseconds because it was the least expensive 64 by 4 DRAM speed that I could find. -- For general comments on this article, feel free to contact Mike Gordillo at s0621126@dominic.barry.edu \H04:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: >> The Metal Shop --- *+* --- with SMS Mike Eglestone << of Diamondback BBS (305)258-5039 Senior Master Sergeant Mike Eglestone has been a devout C= Hardware guru for over ten years and is the Sysop of one of the most active BBS's on the Commnet Commodore BBS network. Mr. Eglestone has written for illustrious magazines in the Commodore 8 bit community such as dieHard magazine and the very distinguished Commodore World. We are pleased to have him entertain our questions pertaining to hardware repair. As always, neither the editors, the staff of disC=overy, nor Mr. Eglestone are responsible for the use or misuse of any information presented in this article. -- >> Dear SMS Mike, >> >> My CMD Hard Drive refuses to undergo its power-up sequence at irregular >> intervals and is belching out a whole series of errors and bad blocks. >> Unfortunately, my drive is long past its warranty period and I have no >> idea what is wrong with it. What do you think? >> >> MG MG, First of all, I would give CMD a call about this even if your drive is out of warranty. They know their drives better than anyone, but with that said, I have run into a total of 5 (was 4) Hard Drives produced by CMD that had cold solder joints on the main circuit board. They were discovered at the connection point for the Power Supply, and were all on the 12VDC connectors for the drive motor. There is a 4 pin DIN plug attached to the main circuit board which is the plug for the power supply to the drive. The connection on the bottom of the board (remove it and turn it over) has a tendency to be bad or loose from poor solder joints (cold joints). This cold or loose joint will cause strange problems with the drive motor, and you will notice SCSI errors, or a sudden accumulation of Bad Blocks. It can even result in the complete failure of the drive to start up, or it might just shut down (stop turning) without warning. The solution is very simple. Remove the drive and circuit board. Turn the board over, and re-solder the joints. Takes about 15 minutes from start to finish... The way the main circuit board is attached to the case is one of the problems. The rear support point is in front of the DIN plug but close to an inch away from it. Each time you insert the plug into the DIN connector, it puts pressure on those connection points and BENDS the board slightly. After a while, the connections loosen up at the solder joints. Cold Joints or joints that were just slightly tacked, will crack or just seperate from the foil. I have now fixed 5 drives with this same problem, and they work perfectly again... CMD is aware of the situation, but they think it's an isolated problem with one batch of boards. I don't think so. I think it's going to happen to all of them sooner or later. That rear support and the location of the DIN seem to be a failure point. -- >> Dear SMS Mike, >> >> I am about to throw my C128's keyboard out my balcony. For the past >> year, a key has been failing to work properly about once a month. I now >> have to put up with at least 12-15 keys that either refuse to work at all >> or require some deep-felt pounding in order to generate a character. >> >> What can I do about this aside from buying another keyboard?? >> >> SK SK, I have cleaned and restored hundreds of keyboards over the last 10 years. I have piles and piles of boards and board parts (64 and 128) sitting around my computer/repair room. Not to mention around 10 extra mother boards for both types of computer. After playing around with the carbon contacts on both the circuit board itself, and the carbon impregnated (rubber contacts) on the end of the key plungers, I have come to the conclusion that the only cleaner required is de-natured alcohol. It works on every part of the board, and works perfectly. When I do a cleaning job, I remove (strip) every key down to its component parts. The keys themselves go into a bleach solution. The rubber pads and plungers go into a pan of denatured alcohol. The key springs (if not rusted) are lightly sprayed with WD-40 and rinsed with alcolol later. If they are rusted, they go into a solution of phosphoric and dichromate acid. Brand name is OSPHO (any True Value Hardware Store has it). The circuit board itself is washed with de-natured alcohol and lightly dried with a blower. Seldom do the carbon spots on the circuit board go bad. If they do, you can use a product which contains silver loaded epoxy, and mix that with carbon powder; Graphite. There is also a spray carbon compound that's available, but I don't remember the name at the moment. It's used by the aircraft industry. Once, I couldn't find any silver loaded epoxy. I just used a hobby type (two part) epoxy and mixed in huge quanitities of graphite. It worked perfectly. Dab on a drop, let it setup, lightly sand the surface to break the glaze.. (wham), a new conductive contacting surface. It was trial and error for a long time, but I now have it down to a science. I normally get four years out of an overhauled keyboard; using original parts and proper cleaning solutions. Oh sure, I have written off a few keyboards as not worth fixing, but that isn't the norm. When one comes in that has been abused to the point that I don't even want to attempt a repair, it becomes parts for other boards. One thing always needs to be done with keyboards (prior to re-assembly). Plug in the circuit board, turn on the computer, then touch each keypad to a contact (one by one) and make sure it's working. This is done by hand. Blank un-assembled (open) circuit board, with keypads held in the fingers. It takes about 5 minutes, and it's well worth the effort involved. NO electrical voltages are present, that will harm you. There are only a couple of volts of DC present, and that is the output of the CIA (Complex Interface Adaptor) which is the Keyboard control chip. You can't run a conditivity test on the rubber keypads with a tester. It's got to have a slight DC voltage present to make it work. A standard VOM will not do the job. The computer itself makes a dandy test bed. -- >> Dear SMS Mike, >> >> Every now and then I'll be sitting peacefully in front of my 128 when >> I start to hear crackling noises out of the monitor's speaker and I'll >> notice that the 40 column screen starts to go wacky on me at the same >> time. Sometimes I'll see color changes and sometimes I'll see little >> random characters appear on screen. The crackling noises appear to always >> be a consistent low pop-pop-click and occur in both 64 and 128 modes. >> A few rare times, 128 mode will crash and hang when these events occur, >> although 64 mode will be a-ok (except for the crackling and popping). >> I know my monitors and cable are working 100% so I am at a loss to explain >> this phenomena. >> >> RR -- RR, There are three possibilities, and one of them is going to sound a bit odd. The 128 uses a two stage video output system. The VIC chip and a seperate Vidio Controller. Both are located inside the metal Box (with lid) in the back left hand side of the circuit board. There is a screw hole dead center of the metal box.. The VIC chip would be my first choice. It's on the right hand side of the box. IC-U21 is the number on the board. The Vidio Controller is mostly for RGB displays and 80 col memory enhancement, but it does work WITH the VIC chip in some area of Composite color IC-U22 (Left hand side of the box). Here is the "odd" choice, but one that I have found in quite a few 128 video and "Crackling Sound" problems.. -> The ON/OFF switch for the 128 itself. As you are aware, the 64 and 128 use a dual voltage power supply system. The computer has the second stage on the Circuit board. That On/Off switch can become dirty inside, or limited in movement enough so that it doesn't make contact perfectly in the ON position. Sometimes it's just a matter of flipping the switch on HARD to make good contact, and sometimes it's necessary to actually remove the switch and clean the contacts internally. On 6 different 128's, where I have come across this problem, I have had to remove the circuit board and File out the switch Hole "larger" at the top because the computer CASE itself was interfearing with the movement of the switch. A "not quite closed" contact in that switch will cause the 9VAC section to ARC slightly. This will drop colors and cause the sound you were talking about. It will affect your internal clocking and cause both video and audio problems. It's always best to check the IC's by substitution. Take a known good IC and replace the one in use. If that doesn't solve the problem then do the same thing with the other chip. Sometimes, just pulling the chip out and putting it back in will fix contact problems. Humidity will cause minor corrosion on the legs of an IC over a period of time. Re-seating the IC will normally fix this sort of thing. A quick shot of contact cleaner in the IC socket never hurts. Those are your three choices guy. Well, there is ONE more, but it's a remote possibility. The DIN socket on the composite video OUT side. You can give that socket a shot of contact cleaner and see if the problem clears up. It will sometimes build up a slight film and cause similar problems to the one you are talking about. Last thing. If you remove the RF shield (the metal cover over the circuit board) LEAVE it OFF. The computer will run cooler and you really don't need that hunk of metal. I normally throw the darn things away. Although it is used as an RF shield and a heat sink, it causes more problems than it solves. SMS MIKE -- Mr. Mike Eglestone may be reached for questions or comments at Diamondback BBS (305)258-5039 or through the editors of disC=overy. \END:::::::d:i:s:C=:o:v:e:r:y:::::::::::::::::i:s:s:u:e::1:::::::::::::::::::: :::::::::::::::::::::::::::::::May 17, 1996:::::::::::::::::::::::::::::::::: ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
magazines/discovery1.txt · Last modified: 2015-04-17 04:35 by 127.0.0.1