#(@)top: ######## ################## ###### ###### ##### ##### #### #### ## ##### #### #### #### #### #### ##### ##### ## ## #### ## ## ## ### ## #### ## ## ## ##### ######## ## ## ## ##### ## ## ## ## ## ##### ## ## ######## ## ## ## ### ## ## #### ## ## ##### #### #### #### #### ##### #### #### #### #### #### ###### ##### ## ###### ###### Issue #11 ################## Version 1.0 ######## December 1995 ------------------------------------------------------------------------------- #(@)contents: Table of Contents Features 6. Speed up RAMLink transfers with the Double-DMA Technique (Reference: dbldma) by Doug Cotton and Mark Fellows RAMLink Designer Mark Fellows and Technical Editor Doug Cotton of CMD describe a way of using a Commodore REU to increase transfer rates of the CMD RAMLink to one-half the speed of the REU transfer rate. 8. The Graphics Toolbox by Stephen Judd (Reference: toolbox) To add another tool to our toolbox, Stephen details a new algorithm for drawing ellipses. Some improvements to the circle routine in a previous column that will enable it to draw perfect circles of any radius is discussed, as well as details on using logarithms to perform division. 10. Design and Implementation of an Advanced Text Editor by Craig Bruce (Reference: zedace) Peer into the internal organization and implementation of an advanced text editor/word processor for the ACE environment. Relevant data structure, global variables, display maintenance, text "sloshing", and algorithms for many editing commands are detailed. Columns 4. Hi Tech Trickery by George Taylor (Reference: trick) Don't let anyone ever tell you the SID chip is only capable of 4 bit sample playback. George Taylor explains using the digi dithering technique to increase the SID's resolution. 12. Hacking Graphics by Rick Mosdell (Reference: gfx) Dig into this overview on how to set up the VIC-II to display Doodle and KOALA format pictures. The two formats are detailed, and similar formats are referenced. Departments 1. The (cough,cough) Hacking Editor (Reference: editor) 2. Input/Output (Reference: io) 3. Newsfront (Reference: news) 5. Hacking the Mags (Reference: mags) 7. UseNuggets (Reference: usenet) 9. Hack Surfing (Reference: surf) 11. Commodore Trivia (Reference: trivia) 13. ? DS, DS$: rem The Error Channel (Reference: error) 14. The Next Hack (Reference: next) ------------------------------------------------------------------------------- #(@)legal: Commodore Hacking Legal Notice Commodore and the respective Commodore product names are trademarks or registered trademarks of ESCOM GmbH. Commodore hacking is in no way affiliated with ESCOM GmbH, owners of said trademarks. Commodore Hacking is published 4 times yearly by: Brain Innovations Inc. 602 N. Lemen Fenton MI 48430 The magazine is published on on-line networks free of charge, and a nominal fee is charged for alternate mediums of transmission. Permission is granted to re-distribute this "net-magazine" or "e-zine" in its entirety for non-profit use. A charge of no more than US$5.00 may be charged by redistribution parties to cover printed duplication and no more than US$10.00 for other types of duplication to cover duplication and media costs for this publication. If this publications is included in a for-profit compilation, this publication must be alternately available separately or as part of a non-profit compilation. This publication, in regards to its specific ordering and compilations of various elements, is copyright(c) 1995 by Brain Innovations, Incorporated, unless otherwise noted. Each work in this publication retains any and all copyrights pertaining to the individual work's contents. For redistribution rights to individual works, please contact the author of said work or Brain Innovations, Inc. Brain Innovations, Inc. assumes no responsibility for errors or omissions in editorial, article, or program listing content. ------------------------------------------------------------------------------- #(@)info: Commodore Hacking Information Commodore Hacking is published via the Internet 4 times yearly, and is presented in both ISO-8859-1 and HTML versions. This and previous issues can be found at the Commodore Hacking Home Page (http://www.msen.com/~brain/chacking/), as well as via FTP (ftp://ccnga.uwaterloo.ca/pub/cbm/hacking.mag/) In addition, the Commodore Hacking mail server can be used to retrieve each issue. To request a copy of an issue, please send the following electronic mail message: To: brain@mail.msen.com Subject: MAILSERV Body of Message: help catalog send c=hacking11.txt quit To subscribe to the Commodore Hacking and receive new issues as they are published, add the following command to you MAILSERV message prior to the quit command: subscribe c=hacking Firstname Lastname msglen (msglen is largest size of file in kilobytes you can receive in an email message. When in doubt, choose 64) example: subscribe c=hacking Jim Brain 100 Although no fee is charged for this magazine, donations are gladly accepted from corporate and individual concerns. All monies will be used to defray any administrative costs, subscribe to publications for review, and compensate the individual authors contributing to this issue. Any persons wishing to author articles for inclusion in Commodore Hacking are encouraged to view the submission guidelines on the WWW (http://www.msen.com/~brain/pub/c-hacking-submit.txt) or via the MAILSERV server (send c-hacking-submit.txt). ============================================================================ #(@)rch: Reading C=Hacking Starting with Issue 11 of Commodore Hacking, the new QuickFind indexing system is utilized to aid readers of the text version in navigating the magazine. At the top of each article or other important place in the magazine, a word prefixed with a special string is present. (See the title of this article for an example. Throughout the magazine, if an article is mentioned, it will be followed by a reference string. For example, if we mentioned this article, we would add (Reference: rch) after the name. By using your favorite editor's search function and searching for the string after the word "Reference:", prefixed by the magic prefix string, will move you directly to the article of choice. To merely skip to the next article in the magazine, search only for the magic prefix string. Some handy indexing strings possibly not referenced anywhere are: top top of issue bottom bottom of issue contents table of contents legal legal notice For those with access to a UNIX system, the command "what" can be run on the issue, which will result in all the article titles being printed. A slightly different magic prefix string "#(A)" is used to delimit sub-topics or main heading in articles. The text after the magic string differs depending on article content. For the Input/Output column (Reference: io), the text after the magic prefix will either be "c" for comment, or "r" for response. In features and columns, a number after the prefix indicates the ordinal of that heading or sub-topic in the article. If a specific sub-topic is referenced elsewhere in the article, a sub-topic reference will be indicated. A reference to "#(A)r" would be written as "(SubRef: r)". As time goes on, the role of this indexing system will be expanded and changed to ease navigation of the text version, but minimize the clutter added by these extra items. ============================================================================ #(@)editor: The Hacking Editor by Jim Brain (brain@mail.msen.com) Two new faces appear in this month's Commodore Hacking. One is its new editor, while the other is its new look. I hope neither causes anyone to worry about the content of the magazine. It's all still here. C=Hacking will continue to provide leading edge technical information about the Commodore computers we all know and love. The magazine will continue to cater to the Commodore computer programmer, whether it be in the areas of sound, graphics, algorithms, disk media access, or communications. However, the role of the magazine continues to expand. It has been shown that many people other than CBM programmers read the magazine, and programmers have requested other information besides technical content be included in the magazine. To this end, Issue 11 contains many new features, including: o "Hacking the Mags" (Reference: mags), which will summarize the other Commodore magazines in the market place. Not everyone can read or subscribe to all the quality CBM publications out there, so this column will alert readers to specific issues that may be of interest. o "Newsfront" (Reference: news), which will bring the Commodore programmer and user up to date on developments in the Commodore community. The Commodore world doesn't stand still, and every programmer should be aware of the newest technologies affecting the CBM line. o "The Error Channel" (Reference: error), which will formalize the process of fixing errors in earlier issues. Hopefully, this will be unnecessary in most issues, but it will be here just in case. o "Input/Output" (Reference: io), which will allow C=Hacking readers space for comments and concerns. Many readers have sent me suggestions and comments, some C=Hacking can implement, and some C=Hacking cannot. This spot will detail which is which and why. o Article separators. As you can see above, each article or column in the magazine is delimited by the special key, followed by a short name of the article. See "Reading C=Hacking" (Reference: rch) in this issue. o Smaller size. The last issue was over 400kB in size, which generated many complaints. There is no need to create such a long issue, when more issues can be published. This issue should comfortably fit on two sides of a 1541 disk, a 1571 disk, or a 1581 disk. o Stable publication dates. Circumstances (college, job hunt), made it hard for the previous editor to maintain a schedule, so no blame is laid, but the magazine does need some stability. Although possibly unrealistic, I am striving to publish C=Hacking quarterly, with the following schedule: Publication Date Submission Deadline March, 1996 February 10, 1996 June, 1996 May 10, 1996 September, 1996 Auguest 10, 1996 December 1996 November 10, 1996 If article submissions keep up, a switch to bi-monthly publication might be warranted, but I won't get too far ahead. o Fully HTML-ized version of the magazine. Issue 11 contains many improvements designed to make the publication of an World Wide Web readable version of the magazine easier. Look for the HTML version of this and older issue at URL: http://www.msen.com/~brain/chacking/. Many people have compared Commodore Hacking to the defunct _Transactor_ magazine, which is encouraging. The new format will hopefully add to the appeal of Commodore Hacking. Although many of you know me or of me through previous Commodore work, this is my first editorship, so please comment on the changes I have made and what your opinions on each are. As the magazine is for you, the reader, it is always important to keep the reader happy. Sadly, some things, like the WWW browser for C=Hacking, did not get done, but there is always next time. Enjoy YOUR magazine, Jim Brain (brain@mail.msen.com) editor ============================================================================ #(@)io: Input/Ouput Obviously, Commodore Hacking depends on the comments and article submissions from the Commodore community to flourish. Everyone sees the articles, but let's not forget those comments. They are very helpful, and every attempt is made to address concerns in them. Address any comments, concerns, or suggestions to: Commodore Hacking 602 N. Lemen Fenton, MI 48430 brain@mail.msen.com (Internet) #(A)c: Need Samples of Samples From: "Clifford \"Paska\" Anderson" <andersoc@saturn.uaamath.alaska.edu> Dear C=Hacking, Hey. Just writing to mention something I'd like to see in C=Hacking, if you can find someone to write about it. I am interested in knowing more about how samples work on the 64, how to play, etc. ----- _ _ _ Si vales, valeo #(A)r: Your wish is granted. Check out this issue's Hi Tech Trickery (Reference: trick) by George Taylor for some insight into playing samples. #(A)c: You Index, I Index, We all Index From: coyote@wakko.gil.net Dear C=Hacking, I would like to offer an idea for the Chacking mag. Every now and then I'll come across a reference to an article in this or that Chacking Issue. I run Zed and load that issue in (Thanks Mr Bruce) and start hunting for the start of the article. This process would be made a lot easier if Chacking used a method of indexing I have seen used in several publications. It involves the search function of most text editors. A 2/3/4 ? letter code (with a delimiter char to prevent unintentional matches) that the reader uses to find the beginning of the article. (Outline of suggestion deleted) I would like to add a personal thanks for all your efforts on behalf of the C= community. Al Anger 13841 SW 139 Ct. Miami Fl. 33186 (305) 233-4689 #(A)r: Fire up that search function in your favorite editor. Issue 11 now contains the QuickFind indexing system that should fit your needs. See "Reading C=Hacking" (Reference: rch) for information on how to utilize the indexing system. We would like to add that C=Hacking appreciates your personal thanks. #(A)c: Are We Talking the Same Language? From: Jack Vander White <ceejack@crl.com> Dear C=Hacking, Noticed something that may be a potential problem and thought I would let you know. Way back when hacking mag started I didn't have Internet access. the first couple of issues were sent to me by fellows who had downloaded them and in downloading had set their terms to translate them to PETSCII. This of course changed the coding in the uuencoding parts of the magazine and made them decode improperly. Since then I have my own access and have re-downloaded them and posted them on my BBS in the original straight ASCII so that those who download them can uudecode the relevant parts and then translate the text for reading. Since different Terminal Programs are using different Translation tables I can see all kinds of problems in this for the average user. Any comment???? Jack VW #(A)r: The HTML version of Commodore Hacking utilizes the ISO-8859-1 text encoding standard, while the text version utilizes its 7-bit subset, commonly called ASCII. Normally, the encoding standard poses little problem, as text uses but a nominal set of characters which can be translated between PETSCII and ASCII. However, as you point out, the uucode format uses more characters, which may or may not be translated correctly. To circumvent this problem, which only occurs in the text version, the files embedded in a Commodore Hacking issue should be uudecoded prior to converting the file to anoy alternate format. ============================================================================ #(@)news: Newsfront * Although not new news, Some still may not know that Creative Micro Designs, Inc., is currently designing the Super64CPU accelerator. This external 3" tall by 6" wide by 2" deep cartridge will allow Commodore computers to execute programs at either 10MHz or 20MHz (actually, 9 and 18 MHz, realistically). The unit uses the Western Design Center's 65C816S CPU, which is object code compatible with the 6502/6510/8502. The CPU, used in the Super Nintendo Entertainment System as well as other products, can be switched between 6502 emulation mode and "native" mode, which allows the following: o access to 16 MB of RAM without bank switching. o 64kB stack. o 64kB zero page (now called "direct access"). o 16 bit registers. o Support for virtual memory systems. The unit is scheduled for production in February, 1996, and will cost ~US$149.00 for the 10MHz unit and US$199.00 for the 20MHz unit. * The following information was relayed to the USENET newsgroup comp.sys.cbm by Jack Vanderhite, editor and publisher of COMMODORE CEE disk magazine: Rather than reply to all the messages asking about DIEHARD I will tell all what has been happening over the last few days. Brian Crosthwaite, Publisher of Diehard, contacted CMD, Loadstar, and Commodore CEE this week with the following form letter faxed to each of us: ----------- Diehard, the Flyer for commodore 8bitters is planning to cease publication and we are looking to transfer our subscription fulfillment. Our number of outstanding subscribers is approximately 8,400 and I would be willing to throw in the balance of the list, totaling approximately 12,000. Please call me at (xxx)xxx-xxxx if you are interested in acquiring these readers and names. Sincerely, Brian L. Crosthwaite Publisher ---------- Each of us did contact Brian for further details. They are bleak. The total number of paper issues due to subscribers is approximately 64,000. This does not count the approximately 1,200 spinner subscribers which would make approximately 10,000 disks due. The cost of publishing alone would amount to approximately $100,000 for printing, layout, disks, ,mail cost, etc. Not taking into account the cost of articles, etc. when asked about money Brian's only comment was "There is none. It's gone." a further complication is that Tom Netsel told me last week that General Media says that Brian has assumed the obligation to deliver the balance of the Gazette subscriptions. I questioned Brian about this. Brian says that general media faxed him the terms of transference of the obligation and that he faxed back an acceptance of the terms. While I have not seen the actual faxes involved it does sound like offer and acceptance of a binding contract from here. Obviously, all of us have rejected this offer. I have been told that there is an issue of Diehard at the printers, probably printed. However, the printing bill alone is over $8,000 plus the cost of mailing. Since there is no money it sits there. If anyone were willing to assume the total obligations they would have to assume a liability of well over $100,000 over the next year before any returns from renewals would even make a dent in this huge obligation. Please Note: I am putting this out as a public message. This is ALL I know. Please do not come back at me asking questions. I have nothing more I can add to this. Jack VW So, if you have outstanding issues of dieHard due you, as the editor does, the fears have been confirmed. However, for those who purchased the dieHard "Spinner" disk, read on for the encouraging news. * The LOADSTAR disk magazine has been recently purchased from Softdisk Publishing by LOADSTAR principles Fender Tucker and Julie Mangham. Now owned by J and F Publishing, a corporation founded by Mr. Tucker and Mrs. Mangham, provide the magazine with even more flexibility Tucker states that now LOADSTAR is "more solvent then ever before". Existing subscribers will see no difference with this change, as Softdisk and LOADSTAR will continue to maintain a close relationship, with Softdisk continuing to handle subscriptions, in addition to other tasks. In related news, J and F Publishing has agreed to fulfill the remainder of the outstading dieHard "Spinner" subscriptions. Although unfortunate that dieHard left its subscribers out in the cold, it is commendable that these subscriptions will be fulfilled with LOADSTAR issues. The agreement will provide one issue of LOADSTAR for every two issues of the Spinner, as the Spinner was a single disk, whereas LOADSTAR is double that. No word has been heard yet on the fate of dieHard paper subscriptions. All 1200 Spinner subscribers should be receiving information about the subscription fulfillment soon. * For those people wishing to use the Internet with their Commodore 64, only to find out that the local Internet Service Provider (ISP) only provides Serial Line Internet Protocol (SLIP) service with no shell account service, help is coming. A prototype Transmissions Control Protocol/Internet Protocol (TCP/IP) protocol stack with SLIP support has been developed by Daniel Dallmann (Daniel.Dallmann@studbox.rus.uni-stuttgart.de) of Germany. Available now via the Internet (ftp://131.188.190.131:/pub/c64), the package is by no means complete, but does include the basic TCP/IP stack, the SLIP driver, and a rudimentary Telnet application. * Another Commodore hardware/software supplier has announced its online presence: Performance Peripherals Incorporated. Maker of the RAMDrive and BB units (BBGRAM, BBRTC, and BBGRam), PPI published an online catalog that users can retrieve via the C=Hacking WWW Site (http://www.msen.com/~brain/pub/PPI_catalog.11.95.txt) and the C=Hacking MAILSERV server. (send PPI_catalog.11.95.txt). In addition to importing FLASH8 (the 8MHz accelerator cartridge from Germany), PPI manufactures CommPort, which is a 6551 UART cartridge (ala Swiftlink) which has the basic 6551 functionality with the addition of switch selectable NMI or IRQ interrupt triggering and switch-selectable $de00/$df00 addressing. * PPI has one more trick up its sleeve. PPI will be carrying Novaterm 9.6, the newest version of Nick Rossi's oft-used terminal program for the C64. The blurb follows: Novaterm 9.6 is a complete terminal emulation program on cartridge for the C64. Novaterm has several features such as 80 column ANSI on a stock C64, and compatibility with CommPort, RAMDrive, BBGRam, and many other hardware devices. Just connect a BBGRam, and Novaterm can use it as a "buffer" for storing text or as a "virtual disk" for quickly and easily downloading files. Definately the perfect setup for Internet usage. And since Novaterm is in cartridge form, the program loads in seconds, not minutes. Novaterm 9.6 is the latest version programmed by NICK ROSSI. Includes autoboot switch. ============================================================================ #(@)trick: Hi Tech Trickery: Sample Dither by George Taylor (yurik@io.org) #(A): Introduction You may know of dithering in graphics. It is when a limited number of colors are used to simulate more. This is done by randomly arranging the pixels so they blend at a distance, creating an average of the shades. Here, screen space is being averaged, and more shades are being produced. In playing samples, time is being averaged, and more bits are being produced. #(A): Dithering Sound Let's say we do the following: lda #8 sta $d418 This code toggles the low bit of the output. lda #9 sta $d418 Over an average of time, this is the same as: lda #8.5 But we can't really do this. sta $d418 This idea can be used to easily do 5 bit sound. Basically, we take a 5 bit sample, shift right, then add 0. If bit 0 was high, it will increment the 4 bit number. Then as this adding takes place, toggling bit 0, it will average out to give half a bit. #(A): Is There a Catch? There is one drawback though. This toggling can be heard as the high frequency square wave it resembles. You must use a high enough sample rate so this can't be heard. Also it takes two bit toggles to create the 5th bit, so you must double the sample rate. In order to play 8.5, for example, you must play 8 and then 9, so the 8 and 9 must take half the normal time, or your sample will play too slow. One other problem is that there is the possibility of overflow. In this case you can use hard clipping on the signal. In other words, the 5 bit sample 31 will be played at 16, so instead play 15. This is actually called pulse width modulation. It is a good example for illustrating sample dithering. For example, you can play TRUE 16 bit sound, even with one bit. To do this, take the 16 bit sample, add a 12 bit random number, then play the high 4 bits of this result. Also remember the clipping problem as mentioned above. @(A): How Is This Like Pulse Width Modulation? The random number range is proportional to the 16 bit sample. If the 16 bit number is high, then it is very likely the 0 bit (toggle bit) is high. It is the random number which allows the toggle bit to change. So now we have 16 bit sound with 16db signal to noise ratio. There are some more advanced technical issues to this. The kind of random number you choose affects the results. You need a triangle density function for perfect linearity (ie., for no distortion). This is the relationship of random numbers in the sequence, and does not affect the probability distribution, which should be equal. The choice of density function is a tradeoff between added noise and linearity. I used pulse density function in my demo, which is non-filtered random numbers, and it's ok but I can still hear some noise pumping. #(A): Conclusion Enjoy the ditherdigi! #(A)5bit: Listing One: 5 bit play routine Memory map: 3: start page of sample 4: end page of sample 5: sample period (remmber to play twice normal speed) fb,fc: pointer to sample start lda 3 sta $fc lda #0 sta $fb ; initialize sample pointer lda #$b sta $d011 ; blank screen for better timing sei ; disable interrupts for better timing play lda ($fb),y lsr sta $d418 ; push sample ldx 5 d dex bne d pha nop adc #0 cmp #$10 beq s1 sta $d418 bpl s s1 nop nop nop s pla ldx 5 d1 dex bne d1 iny bne play inc fc lda fc cmp 4 bne play lda #$1b sta $d011 cli end rts #(A): References Consult the proceedings of the ACM for further info on digi dithering. ============================================================================ #(@)mags: Hacking the Mags Not everything good and/or technical comes from Commodore Hacking, which is as it should be. (I still think we have the most, though...) Thus, let's spotlight some good and/or technical reading from the other Commodore publications. If you know of a magazine that you would like to see summarized here, let C=Hacking know about it. These summaries are only limited by Commodore Hacking's inability to purchase subscriptions to all the Commodore publications available. We are very grateful to those publications that send complimentary copies of their publications for review. #(A): COMMODORE CEE Volume 1, Issues 1 and 2 came all packaged as one "mega-issue". This particular double issue should be renamed the memory map issue, with I/O and/or memory maps for the VIC, 64, 128, and PET computers. Information on 6522 bugs and on the 6526 CIA chips that was cut from the final compilation of the Commodore 64 Prorammer's Reference Guide is of interest to Commodore Haking readers. Some of the information is culled from the Internet: the 64 memory maps, the info on the 6522, and a list of all the CSG produced IC numbers with descriptions. Of course, these files are also available on the Internet, if you have access. Howver, for those who don't know where to look or for those without access, the information is welcome. Issue 3 has a PCX to GEOPaint converter, much like _LOADSTAR_, and Issue 4 will begin a column on PAL to NTSC program conversions. One thing we'd like to see at Commodore Hacking is a better menu program, as the current one is somewhat hard to navigate. #(A): Commodore World Issue 10 just arrived at the computer room, with a snazzy front cover. Slick paper aside, the picture of Al Anger's Tower 128 was a masterpiece. Editor Doug Cotton spews about the hype of Windows 95, and the first ads for the Super 64 CPU accelerator are present. If you're into hardware mods, you can't miss page 4, which shows some other Al Anger hacked Commodore creations. Jim Butterfield's 4 page 65XX ML reference is useful for the newer programmers, and Doug Cotton's Assembly Line topic of serial routines will help those disk I/O challenged in the crowd. This issue details the high level routines, while #11 will tackle the low level disk I/O. Maurice Randall goes over event handling in GEOS, while Al Anger details how to disable the internal 1571D in the C128D. Gaelyne Moranec touches on the Internet nitty-gritty of learning UNIX commands and includes a table of UNIX-like commands found in ACE and LUnix. At the end, though, C=Hacking's burning question is: What hangup does Doug have with those abstract graphics sprinkled throughout the mag? There's nothing wrong with them, but some look like those psycho-analyst inkblot test cards. #(A): Driven Driven 9 contains a rundown on USENET (written by Jim Brain), which will help those Internet "newbies". For those doing cross development, the review of the PC<->C64/C128 networking system called 64NET by Paul Gardner- Stephen might help some get object code from the PC to the 64/128. Eddie Bourdon has some info on GEnie, including what Commodore support is available. Driven 10 presents some useful WWW addresses, while XMikeX and Pegasus tackle the issues of apathy and pessimism in the Commodore community. Both make for good reading, but the best (in our opinion) was the pessimism piece. How many times have YOU been laughed out of CompUSA for mentioning that modem or SCSI drive was for a Commodore? #(A): LOADSTAR Issue 138 just finished loading on the 1581 disk drive, and the disk is packed with information. Fender Tucker goes into much detail on the recent changes at LOADSTAR and its new Publishing company, J and F Publishing. Of interest to programmers is the PCX to GEOPaint converter program, written by Fender Tucker and Doreen Horne. Some details on Al Angers machines that are shown in Commodore World are related. Jeff Jones presents a simple program pause routine, which fiddles with the NMI interrupt, and gives out source code as well. The Internet 101 series takes a month off from the LOADSTAR letter in #28, but is expected back next month. Lastly, Dave Moorman presents his fractal generator called FRACTAL MOUNTAINS. C=Hacking couldn't get it to work, but we think it's user error. #(A): LOADSTAR 128 In Issue 29, Fender apologizes for not paying enough attention to the 800 LOADSTAR 128 subscribers. Of interest to programmers is the program listing pause program on the issue, but the rest is pretty light stuff, not to knock LOADSTAR. Different audiences need different material. #(A): Vision In Issue 7, Rick Mosdell has an article on graphics formats, updated and reproduced in this issue (Reference: gfx). There is some information from USENET reproduced, and a list of FTP sites as posted to USENET is also presented. Not much technical content in here, but C=Hacking was impressed with the graphics, music, and stories in the mag. Besides, everyone needs some time to enjoy the machine. Other magazines not covered in this rundown include _The Underground_, _Gatekeeper_, _Commodore Network_, _64'er_, _Atta Bitar_ (_8 bitter_), as well as those C=Hacking is simply not aware of. As soon as we can snag a copy of any of these, or get the foreign language ones in English :-), we will give you the scoop on them. ============================================================================ #(@)dbldma: Speed up RAMLink transfers with the Double-DMA Technique by Doug Cotton (cmd-doug@genie.com) and Mark Fellows #(A): Introduction When CMD designed the RAMLink, we tried to make the system as fast as possible, but costs and complexity prohibited us from duplicating the operation of the DMA operation found in the Commodore RAM Expansion Unit (REU), The 8726 DMA controller found in the REU is a very complex item that allows the REU to transfer one byte per 1 MHz CPU clock cycle (1 microsecond). On the other hand, the RAMLink uses the 6510/8502 CPU load and store operations to transfer memory from the RAMLink memory to main memory. For the user who uses RL-DOS and RAMDOS, the difference is not noticeable, because although the RAMLink transfer is slower, RAMDOS continually pages its code in and out of main memory, effectively slowing its effective transfer speed down significantly. But, what if the programmer isn't using RAMDOS? Then, the speed of the RAMLink becomes an issue. The RAMLink takes about 8 cycles to perform a transfer of a byte, while the REU does it in 1. This is significant. However, if a user owns both a RAMLink and an REU, there is a way to boost the transfer rate of the RAMLink via software. The method is called Double-DMA. #(A): Double-DMA Description Basically, the process is quite simple. Since the REU has the ability to transfe memory at 1 byte/microsecond, you can use the REU DMA to transfer memory from the RAMLink to main memory. To understand how we can do this, remember that the normal RL-DOS transfer routines use the CPU to perform the memory transfer. Well, to do that, at least some of the RAMLink RAM must be mapped into main memory. To be exact, 256 bytes is mapped in. So, to utilize the Double-DMA technique, the programmer simply makes the appropriate 256 bytes of RAMLink memory to be transferred visible in the main memory map, uses the REU to transfer that 256 bytes to the REU, and then uses the REU to transfer the 256 bytes in the REU to its destination in the main memory map. Thus, the Double-DMA technique will allow the RAMLink to transfer data at rouyghly 1/2 the speed of the REU, or 3-4 times faster than using the CPU to perform transfers. #(A): The RAMLink memory map To achieve this transfer speed gain, the programmer must forego RL-DOS usage and write specialized transfer routines. To do that, we need to discuss how the RAMLink maps itself into main memory and detail the various RAMLink registers needed to make this feat possible: Address Description ------- ----------- $de00 256 bytes of data (See $dfc0-$dfc3 for more information) $df7e write to this location to activate the RAMLink hardware $df7f write to this location to deactivate the RAMLink hardware. $dfa0 lo byte of requested RAMCard memory page $dfa1 hi byte of requested RAMCard memory page $dfc0 write to this location to show RL variable RAM at $de00 (default) $dfc1 write to this location to show RAMCard memory at $de00 $dfc2 write to this location to show the RAM Port device $de00 page at $de00 $dfc0 write to this location to show Pass-Thru Port dev. $de00 page at $de00 For all locations that have the description "write to this address...", the program can safely write any byte to those locations, as the RAMLink hardware simply waits for an access, not any particular byte to be written. #(A): Order of Operations Although the Double-DMA technique relies on use of the REU, it is beyond the scope of this article to detail how to access the REU RAM under programmatic control. For more information on transferring data from the Commodore 128/64 and the 17XX REU, refer to the back of a REU owner's manual. The following steps will realize the Double-DMA method: Notes: P = PAGE in RAMCard RAM to be transferred to/from A = PAGE of RAM in main memory to be transferred to/from X = single page of memory in REU used as temp RAM 1) if computer = 128, set up correct RAM bank 2) make I/O visible in main memory 3) sei 4) sta $df7e - activate RAMLink 5) lda #<P 6) sta $dfa0 7) lda #>P 8) sta $dfa1 9) sta $dfc1 - make $de00 show PAGE of RAM on RAMCard Now, with the RAMLink hardware enabled in this way, the REU registers are also visible, so one can do a double DMA transfer at this point. There are two choices: Transfer A->P: 10) set up REU for A->X transfer 11) initiate REU DMA transfer 12) set up REU for X->$de00 transfer 13) initiate REU DMA transfer Transfer P->A 10) set up REU for X->$de00 transfer 11) initiate REU DMA transfer 12) set up REU for A->X transfer 13) initiate REU DMA transfer Now, to go on: 14) If more byte need transferrring, A=A+1, P=P+1, goto 5 15) sta $dfc1 - restore contents of $de00 15) sta $df7f - deactivate RAMLink hardware 16) if computer = 128, restore bank 17) restore I/O visibility if needed 18) cli #(A): Address Translation To effectively use the Double-DMA technique, a programmer will want to set up a DACC partition in the RAMLink for use as external RAM. The programmer will need to determine the start address of the partition with the RL-DOS G-P command (or its sister command, G-[shift]P) This command will return the address of the DACC partition, or will it? The answer is: Maybe. If a user has inserted an REU into the RAMLink RAM port and has the Normal/Direct swittch set to Normal, RL-DOS uses REU memory as the lowest RAM in the RAMLink memory map. However, when directly accessing the RAMLink and bypassing RL-DOS, the REU is not mapped into the RAMLink memory map. So, for such a condition, the code that determines the start of the DACC partition must SUBTRACT the size of the REU from the address returned by the G-P command. It's non-utopian, but the program need only do this once. However, for such an REU configuration, one must take care to ensure that at least 256 bytes of REU RAM is available and not already in use before utilizing the Double-DMA technique. #(A): Performance Craig Bruce, who has implemented this technique in his ACE operating system, provides the following performance figures for different access techniques: Type Bandwidth Latency Notes (bytes/sec) (~usec) ------------- --------- ------- ----- REU 1,007,641 65.8 REU in Direct mode REU thru RL 1,007,641 77.8 REU in RAM Port in Normal mode RAMLink 105,792 199.2 Regular RAMLink access RL with REU 372,827 319.8 Double-DMA Internal RAM0 120,181 44.2 Zero-page Internal RAM1 80,283 56.3 All main memory except zero-page So, using this technique in ACE results in a 3.7x increase in transfer speed. For some applications, that is well worth the trouble. #(A): Conclusion Obviously, CMD recommends that the RL-DOS be used for most operations, but we realize that some programmers simply need faster transfer rates. The Double-DMA technique should provide the speed needed from the RAMLink. Obviously, since this technique bypasses RL-DOS, code using it can potentially corrupt RAMLink memory if errors occur or if the technique is improperly used. When using the technique, we recommend extensive testing using various DACC partitions and different REU configurations to ensure proper operation. #(A)ddcode: Double-DMA Code Following is a set of functions that will perform transfers using Double-DMA. They are copied from the routines used in Craig Bruce's ACE operating system, Release 14, which incorporates the Double-DMA method. We thank Craig for the code below: ; Name: Double-DMA memory transfer ; Author: Craig Bruce ; Date: 1995-12-4 ; Description: The following routines use the Double-DMA technique to transfer ; memory to/from main RAM and the RAMLink. If no RL is present, ; normal CPU transfer methods are utilized. ; ; Variables: [mp] holds the address of RAMCard memory to transfer ; ramlinkNearPtr hold the address of main memory to transfer ; ramlinkLength is length of data to transfer ; ramlinkOpcode = $90: main memory -> RL ; = $91: RL -> main memory reu = $df00 rlActivate = $df7e rlDeactivate = $df7f rlSram = $dfc0 rlPageSelect = $dfa0 rlPageActivate = $dfc1 rlPageData = $de00 ramlinkOpcode .buf 1 ramlinkLength .buf 2 ramlinkNearPtr .buf 2 ramlinkMpSave .buf 3 ramlinkZpSave .buf 2 ramlinkOp = * ;( [mp]=farPtr, ramlinkNearPtr, ramlinkLength, ramlinkOpcode ) lda mp+0 ldy mp+1 ldx mp+2 sta ramlinkMpSave+0 sty ramlinkMpSave+1 stx ramlinkMpSave+2 lda zp+0 ldy zp+1 sta ramlinkZpSave+0 sty ramlinkZpSave+1 lda ramlinkNearPtr+0 ldy ramlinkNearPtr+1 sta zp+0 sty zp+1 clc lda mp+1 adc aceRamlinkStart+0 sta mp+1 lda mp+2 adc aceRamlinkStart+1 sta mp+2 - lda ramlinkLength+0 ora ramlinkLength+1 beq + jsr rlTransferChunk jmp - + lda ramlinkMpSave+0 ldy ramlinkMpSave+1 ldx ramlinkMpSave+2 sta mp+0 sty mp+1 stx mp+2 lda ramlinkZpSave+0 ldy ramlinkZpSave+1 sta zp+0 sty zp+1 clc rts rlTrSize .buf 1 rlTransferChunk = * ;( [mp]=rlmem, (zp)=nearmem, rlLength, rlOpcode ) ;** figure maximum page operation lda ramlinkLength+1 beq + lda #0 ldx mp+0 beq rlTrDo sec sbc mp+0 jmp rlTrDo + lda mp+0 beq + lda #0 sec sbc mp+0 cmp ramlinkLength+0 bcc rlTrDo + lda ramlinkLength+0 ;** do the transfer rlTrDo = * tay sty rlTrSize jsr rlPageOp ;** update the pointers and remaining length clc lda rlTrSize bne + inc mp+1 inc zp+1 dec ramlinkLength+1 rts + adc mp+0 sta mp+0 bcc + inc mp+1 + clc lda zp+0 adc rlTrSize sta zp+0 bcc + inc zp+1 + sec lda ramlinkLength+0 sbc rlTrSize sta ramlinkLength+0 bcs + dec ramlinkLength+1 + rts rlPageOp = * ;( [mp]=rlmem, (zp)=nearmem, .Y=bytes, ramlinkOpcode ) php sei sta rlActivate lda mp+1 sta rlPageSelect+0 lda mp+2 sta rlPageSelect+1 sta rlPageActivate lda aceReuRlSpeedPage+3 bne rlPageOpReu ;xxx dependency on aceMemNull==0 rlPageOpNonReu = * tya clc adc mp+0 tax lda ramlinkOpcode cmp #$91 bne rlPageOpWrite dex dey beq + - lda rlPageData,x sta (zp),y dex dey bne - + lda rlPageData,x sta (zp),y jmp rlPageOpContinue rlPageOpWrite = * dex dey beq + - lda (zp),y sta rlPageData,x dex dey bne - + lda (zp),y sta rlPageData,x rlPageOpContinue = * sta rlSram sta rlDeactivate plp rts rlPageOpReu = * ;( [mp]=rlmem, (zp)=nearmem, .Y=bytes, ramlinkOpcode ) ;** ramlink hardware already switched in ldx #1 tya beq + ldx #0 cmp #0 ;xx cut-off value bcc rlPageOpNonReu + ldy ramlinkOpcode cpy #$90 beq + ldy #$90 ;rl->reu->intern jsr rlPageOpReuRl ldy #$91 jsr rlPageOpReuIntern jmp ++ + ldy #$90 ;intern->reu->rl jsr rlPageOpReuIntern ldy #$91 jsr rlPageOpReuRl + sta rlSram sta rlDeactivate plp rts rlPageOpReuIntern = * ;( .AX=bytes, .Y=op ) sta reu+7 ;len stx reu+8 sty temp1 pha lda zp+0 ldy zp+1 sta reu+2 sty reu+3 lda aceReuRlSpeedPage+0 ldy aceReuRlSpeedPage+1 sta reu+4 sty reu+5 lda aceReuRlSpeedPage+2 sta reu+6 .if computer-64 ldy vic+$30 lda #0 sta vic+$30 .ife lda temp1 sta reu+1 .if computer-64 sty vic+$30 .ife pla rts rlPageOpReuRl = * ;( .AX=bytes, .Y=op ) sta reu+7 ;len stx reu+8 sty temp1 pha lda mp+0 ldy #>rlPageData sta reu+2 sty reu+3 lda aceReuRlSpeedPage+0 ldy aceReuRlSpeedPage+1 sta reu+4 sty reu+5 lda aceReuRlSpeedPage+2 sta reu+6 .if computer-64 ldy vic+$30 lda #0 sta vic+$30 .ife lda temp1 sta reu+1 .if computer-64 sty vic+$30 .ife pla rts ============================================================================ #(@)usenet: UseNuggets COMP.SYS.CBM: The breeding ground of programmers and users alike. Let's see what topics are showing up this month: #(A): We Want More Power! CMD's announcement of the Super64 CPU accelerator got things stirred up in the newsgroup. When it was announced that the initial product would run on a C64 or on a C128 in 64 mode only, some angry C128 128 mode users vented all over the place. Everything from people wondering aloud what extra work the 128 version would require to threats of non-purchase of the unit ensued. Then, just as the first wave of fighting subsided, the next wave started, programmers worried about RAM transfer speed bottlenecks questioned CMD's decision not to include a DMA device on the unit to speed data transfers. CMD's response: From: Doug Cotton <cmd-doug@genie.geis.com> Newsgroups: comp.sys.cbm Subject: Re: Power Users! Date: 28 Nov 1995 00:59:26 GMT Organization: Creative Micro Designs, Inc. There were some earlier questions about how fast memory transfers could be accomplished with the accelerator, and at least one individual emailed me over the lack of a DMA controller. I obtained some figures from Mark concerning this. Presently, the DMA transfers using an REU transfers a byte in 1 microsecond. The accelerator can achieve this same speed when transferring data from either on-board static RAM, or from expansion memory (slower DRAM) to the host computer RAM. Transfers internally (from static RAM to static RAM) will take .35 microseconds per byte (350 nanoseconds). Transfers from RAMLink RAMCard RAM (direct style) to the host computer RAM will take about 2 microseconds per byte. The only figures I don't have yet are for transfers between on-board static RAM and expansion DRAM, but this will be governed by the speed of the DRAM itself, and the number of wait-states required. It definately will be faster than 1 byte per microsecond though. So the only thing slower than a current DMA operation is transferring to and from RAMLink RAMCard memory, which is still pretty impressive at half the speed of present DMA transfers. Given these speeds, the cost of high-speed DMA controllers ($$$$), and a real lack of anywhere to put one on the main board, I think going without a DMA controller is reasonable. If you really want one, though, there's always the high-speed expansion port, and a do-it-yourself project. Doug Cotton Notice the tiny "high speed expansion port" mention at the end. Reports indicate that such a port or ports will definitely appear on the unit, but it is still undetermined whether a single connector or a small expansion bus will be utilized. Commodore Hacking recommends the latter, as more options for hardware mods are available. #(A): Let's all design the Commodore 64 Laptop! Yes, the dreamers are at it once again. Starting in late October, the net was abuzz with thoughts on what should be included on a Commodore Laptop. The designs were flying fast and furious, with many different features discussed. It was agreed that the laptop would need to be a power sipper and have an LCD screen and a keyboard. However, that was where agreement ended. Some of following items were bantered about: CPU: o "really fast" 6510 o 65C816S Disk: o FLASH RAM cards. o built in hard drive o low power 1581 or CMD FD2000/4000 RAM o definitely more than 64kB, but disagreement as to how much more. Video o VIC-II compatibility with more modes. o VIC-III as found in Commodore 65 Sound o Built in stereo SIDs o Quad SIDs So, on and on it went. Some got down to the nitty gritty of planning designs for chips. Some wanted to put the SIDs into one chip, while others wanted a SID/VIC/CPU single chip solution. It's December, and the thread is still going strong, but a few great things have surfaced, which is why you can't just discount this type of dreaming: o Someone posted the procedure for modifying the C64 to run on battery power. o A few people started looking into how much money such designing would require. o Most people who thought disk media should be included agreed that the CMD FD drive could/should be used. o Everyone woke up and noticed that the NMOS CPU process used for the fabbibng of the CBM chips was power hungry and ill-suited to battery operation. C=Hacking encourages users to answer the quetion: My dream Commodore laptop computer would include.... Send you entries to Commodore Hacking (brain@mail.msen.com) with the subject "LAPTOP". We'll print the best entries next issue. Everyone seems to think that CMD is going to have one in development before long. Dunno. Commodore Hacking has heard rumors of what is going on at CMD, but we haven't heard about the laptop project. Of course, we're not SPECIAL or anything.... :-) #(A): The Tower of Power It seems Al Anger's (coyote@gil.net) Tower 128 picture on Commodore World's Issue 10 cover got everyone excited. A couple of people were sending Al email about it, Commodore Hacking asked some questions, and some USENETters were deciding how to do it themselves. Al states that $2000 would just about cover it, which turned a few enquiring minds away, we're sure. Still, the reasons given for wanting a tower were solid. Commodore users are getting tired of all the clutter and mess cables, power cords, expansion extenders, Swiftlink cartridges, etc. make in the computer room. C=Hacking notes that at least one manufacturer produces tower 64 systems, but the cost is evidently more than what most folks are willing to fork over (~US$300 - US$550). So, everyone is waiting for the cost to come down.... #(A): Dave Letterman, Eat Your Heart Out! The latest thread is the top ten list of games. Everyone is submitting their 10 most favorite games for the CBM machines. (Is anyone compiling these?) Anyway, it turns out this thread has a nice side effect. People are reminiscing about the old games, and the Commodore users are noting that the new games "just aren't as good". Here, here! So, that wraps up the USENET this time. We try to keep an eye out for stuff of interest, but drop us a line if you think we might miss an IMPORTANT topic... ============================================================================ #(@)toolbox: The Graphics Toolbox: Ellipses by Stephen L. Judd (sjudd@nwu.edu) #(A): Introduction After a much needed break from Commodore 64 programming, I thought it would be nice to construct another algorithm for the 2D graphics toolbox. Since we did circles last time, a natural successor would be an algorithm to draw eclipses. We will first review the circle algorithm, and then build upon it to draw eclipses. You may recall that the algorithm had problems with small-radius circles. There is a very easy way to fix this, so we will cover that issue as well. #(A): Circles Recall that the equation for a circle is x^2 + y^2 = r^2 After taking differentials of both sides, we find that dy = -x/y dx That is, if we take a step of size dx in the x-direction, we in principle want to take a step of size dy in the y-direction. Next we start at the top of the circle, so that y=r and x=0. We start increasing x in step sizes of one. We only care about step sizes of one, since our basic unit is now a pixel. The y-coordinate is going to start piling up these dy's, and at some point the integer part of y will increase, and we get a new y-coordinate for the pixel. The idea, then, is to keep adding the dy's together, and once their sum is greater than one, we decrease y (remember that y starts at the top of the circle). The sneaky way to do this is to treat y as an integer "constant". Then it is very easy to add the dy's together, since they have a common denominator equal to y. So really all we need to do is start adding x-coordinates together, and once their sum is larger than y, we decrease y and hang on to the remaining fractional part of dy. The algorithm then looks like: y=r x=0 a=r loop: x=x+1 a=a-r if a<=0 then a=a+y:y=y-1 plot (x,y) if x<y then loop: Now, Chris McBride pointed something out to me. As you may recall, the algorithm breaks down for small r. Chris said that if a is initially set to r/2 instead of r, the algorithm works perfectly. Why is that? Recall that we add dy to itself until it is greater than one. Wouldn't it make more sense to add dy to itself until it is greater than 0.5? That would have the effect of rounding things up. Thus, starting at r/2 is like adding 0.5 to the fractional part of y -- it is the difference between INT(y) and INT(y+0.5). Thus, the above line a=r should be changed to a=r/2 for a perfect circle every time. Thus, this corresponds to adding an LSR to the machine code. Incidentally, this fix appeared in an earlier C=Hacking, but it was placed in such a crazy place that you probably never saw it. #(A): Ellipses, HO! Now we can move on to eclipses. Since ellipses are simply a squashed circle, it seems reasonable that we could modify the above circle algorithm. So, let's get to it! Everyone knows the equation of an eclipse: x^2/a^2 + y^2/b^2 = 1 Upon taking differentials of both sides we have, 2*x*dx/a^2 + 2*y*dy/b^2 = 0 or, equivalently, dy = -b^2/a^2 * x/y * dx As you can see, life becomes suddenly becomes more complicated by a factor of b^2/a^2. Furthermore, with an eclipse we only have reflection symmetries through the x- and y-axis. In the circle algorithm we could get away with just drawing an eighth of the circle, but now we have to draw a full quarter of the eclipse. We will start drawing the eclipse at x=0, y=b, so that initially x will increase by one at each step, and y will wait a few steps to increase. At some point, though, we will want y to increase by one at each step, and x to wait a few steps before increasing; in the circle algorithm we just quit once we reached this point, but now we are going to need an equation for dx: dx = -a^2/b^2 * y/x * dy In the circle algorithm, we used a single variable to count up and tell us when it was time to increase y. Perhaps your intuition suggests that we can do an eclipse with _two_ variables; mine said the same thing, so that is exactly what we will do. First, let us assume we have a way of calculating b^2/a^2: E = b^2/a^2 I will suggest a way to perform this calculation later. Let's write out the first few terms in the dy summation, starting at x=0, y=b: dy1 + dy2 + ... = -E * (x0 + x1 + x2 + x3 + ...)/y = -E * (0 + 1 + 2 + 3 + ...)/b = - (0 + E + 2E + 3E + ...)/b So, the basic structure of the algorithm is: add up 0, E, 2E, etc. until the sum is larger than y. At that point, reset the counter, keeping the remainder, and decrease y. This is where the two variables come in: X=X+1 T2=T2+E T1=T1+T2 IF T1>=Y THEN T1=T1-Y:Y=Y-1 Do you see how it works? T2 simply takes on the values 0, E, 2E, 3E, etc., and T1 is the counter. Furthermore, you can see that once T2 is larger than Y, dy will be larger than one at each step. We need a new algorithm to continue the calculation, and it turns out to be quite simple. Look at the expression for dx above. We could calculate a^2/b^2, but somehow that goes against the spirit of the calculation so far. Let's instead rewrite dx slightly: dx = - y/(E*x) * dy Here we have simply written a^2/b^2 as 1/(b^2/a^2) = 1/E. But E*x is exactly the variable T2 above, so we can continue the calcuation without even stopping for breath: Y=Y-1 T1=T1+Y IF T1>=T2 THEN T1=T1-T2:X=X+1:T2=T2+E (remember that T1 keeps track of the fractional part of y). So, we now have a complete algorithm for drawing an eclipse: 0 REM ELLIPSE ATTEMPT #N SLJ 11/3/95 10 A=150:B=16:E=B*B/(A*A) 20 X=0:Y=B:T1=0:T2=0.5 30 GRAPHIC1,1:SLOW:X0=160:Y0=100:DRAW1,X0+A,Y0:DRAW1,X0,Y0-B 40 X=X+1:T2=T2+E 50 T1=T1+T2 60 IF T1>=Y THEN T1=T1-Y:Y=Y-1 70 DRAW1,X0+X,Y0-Y 80 IF T2<Y THEN 40 90 Y=Y-1 100 T1=T1+Y 110 IF T1>=T2 THEN T1=T1-T2:X=X+1:T2=T2+E 120 DRAW1,X0+X,Y0-Y 130 IF Y>0 THEN 90 Lines 40-80 are the top part of the eclipse, and lines 90-130 handle the bottom part. Note that T2 starts at 0.5, to round off the calculation in the same spirit as we did in the circle algorithm. Naturally, this algorithm has a few limitations. In line 30 the start and end points are plotted, so you can see how close the algorithm really is. In my experiments it occasionally missed the endpoint by a pixel or two. As usual, I was a little too lazy to investigate possible ways to get around this. If you require a perfect eclipse, you need to start the calculation at x=0, y=b and run it forwards (e.g. lines 40-80 above), and then do another, similar calcuation, starting at x=a, y=0, and running backwards. That is, for the second calculation, calculate E2=a^2/b^2, and then run the algorithm just like lines 40-80, interchanging X and Y. Now we need to translate this algorithm into assembly. I am going to make a few assumptions: first, that everything fits in a byte. In particular, I require that b^2/a < 256. This insures that b^2/a^2 < 256, and also insures that T2 will not overflow (note that when x=a, T2=E*a, e.g. T2=b^2/a). What this means is that eclipses can't be too squashed. Next, we need to deal with the fraction E=b^2/a^2. Any number like this consists of two parts, an integer part plus a fractional part (e.g. a number and a decimal). So, let's split E into two parts, EL and EH, where EL represents the decimal part and EH the integer. Now our addition consists of adding together the fractional parts, and if there is an overflow, increasing the integer part. For example, if E=1.62, then EH=1 and EL=0.62. We add EL to our number, and if it is greater than one, we carry the one to when we add EH to our number. The best thing to do is to represent EL as a fractional part of 256. That is, our EL above should really be 0.62*256. This way, carries and overflows will be handled automatically (this will become clear in a moment). Let me give some pseudo-assembly code and we'll push off the explanation until later: 35 GOTO 200 190 REM *********************** 200 XM=0:YM=B:X=128:Y=0:EH%=INT(E):EL%=INT((E-EH%)*256+0.5) 210 XM=XM+1 220 C=0:A=X:A=A+EL%:IF A>255 THEN A=A-256:C=1 230 X=A:A=Y:A=A+EH%+C:Y=A 235 A=A+T1 240 IF A>=YM THEN A=A-YM:YM=YM-1 250 T1=A:DRAW1, X0+XM, Y0-YM 260 IF Y<=YM THEN 210 265 T2=Y:A=T1 270 YM=YM-1 280 A=A+YM:IF A<T2 THEN 300 290 A=A-T2:T1=A:XM=XM+1:A=X:C=0:A=A+EL%:IF A>255 THEN A=A-256:C=1 295 X=A:A=T2:A=A+EH%+C:T2=A:A=T1 300 DRAW1, X0+XM, Y0-YM 310 YM=YM-1:IF YM>=0 THEN 280 XM and YM are the x and y coordinates of the point to be plotted. Note that in line 200 X starts at 128, and this again is to round up all our calculations; compare to line 20, where we started T2 at 0.5. In the above code I store T2 in the X and Y registers for the first part of the code. Note that in lines 220 and 290 there is some extraneous code to simulate things that in assembly are taken care of by the 6502. Note also that the comparison in line 260 has been changed from < to <=. This makes the branch easier, and I'm not sure how it affects the calculation (I didn't notice any difference in the few runs I tried it on). Moving through the code, we increase x, and then add the decimal part of E to the counter. Then we add the integer part of E to the counter, along with any carries. If the integer part of the counter is greater than y, it is time to decrease y and reset the counter. Moving to the second part of the code, we do a little rearranging in line 265. Really a better thing to do would be to let A=T1-T2, so that the compare in line 280 becomes simpler. Anyways, note that the Y register becomes freed up at this point. From here on, it is pretty much the same thing as before. The full assembly code is then: ;Ellipse SLJ 11/3/95 Assumptions: ;0->XM B->YM, x- and y-coordinates ;0->T1 ;EL and EH contain remainder and integer parts of E, resp. LDX #128 LDY #00 CLC L1 INC XM TXA ADC EL TAX TYA ADC EH TAY ADC T1 CMP YM BCC :CONT1 SBC YM DEC YM :CONT1 STA T1 JSR PLOT CPY YM BCC L1 STY T2 LDA T1 SBC T2 DEC YM L2 ADC YM BCC :CONT2 SBC T2 STA T1 INC XM TXA ADC EL TAX LDA T2 ADC EH STA T2 LDA T1 :CONT2 JSR PLOT DEC YM BPL L2 ;Assuming y<128 #(A): Logarithms Finally, we need a way of calculating b^2/a^2. I suggest using logarithms for this. I do believe I discussed this concept in an earlier issue of C=Hacking. Nevertheless, the idea is that if x = b^2/a^2 then log(x) = 2*log(b) - 2*log(a) so that x = exp(2*(log(b) - log(a)) Thus, three tables need to be created: one for log(x), and one each for the integer and remainder parts of e^(2*x). Now, to improve accuracy, the first table might be a table of f(x)=222/log(128) * log(x/2). This constant is chosen so that f(255) is roughly 255. 222 was chosen because the inversion (i.e. the e^x part) works best at that value. This pretty much assumes that x is not zero or one, either. You can of course use more tables for somewhat better accuracy. One really nice thing about this is that you don't have to worry about squaring things, since that part can be taken care of automatically in logarithm space. On the downside, we are restricted even further by the types of numbers we can divide (e.g. log(a)-log(b) can't be larger than 127 or so). Division then consists of a table lookup, subtraction of another table lookup, and two more table lookups. Here is a short program to demonstrate the use of logs in this sort of division, and a very rough feel for the type of accuracy to expect -- note that it doesn't compare decimal parts, or convert the decimal parts into fractions of 256, etc.: 1 FAST:PRINT"[CLR]" 10 DIM L(256),EI(256),ER(256):FC=222/LOG(128) 20 FOR I=1 TO 256 25 PRINT "[HOME]"I 30 L(I)= INT(FC*LOG(I/2)+0.5):IF I=1 THEN L(I)=0 40 S=I:IF I>127 THEN S=I-256 50 EX=EXP(2*S/FC):IF EX>256 THEN PRINT"WHOOPS! EX="EX"I="I 60 EI(I)=INT(EX+0.5) 70 ER(I)=EX-EI(I) 80 NEXT I 90 EI(0)=1:ER(0)=0 100 FOR A=2 TO 250 110 FOR B=2 TO 250 120 X=L(B)-L(A) 123 IF X>127 THEN PRINT"OOPS:A="A"B="B"X="X 126 IF X<0 THEN X=X+256 130 A1=EI(X)+ER(X):A2=B*B/(A*A):IF A2>255 THEN B=600 135 BL=INT(A2+0.5)-INT(A1+0.5) 140 PRINT A;B,A1;A2,"ERR="INT(A2+0.5)-INT(A1+0.5) 150 NEXT:NEXT #(A): Conclusion Sorry, no 3D graphics this time around. Watch for a full-screen, hires bitmapped solid 3D virtual world sometime in the not too distant future. Otherwise, may your ellipses never be square :). ============================================================================ #(@)surf: Hack Surfing For those who can access that great expanse of area called the World Wide Web, here is some new places to visit that are of interest to the Commodore community. In early 1994, when the US Commodore WWW Site started, the number of sites online that catered to Commodore numbered in the 10's. Now, the number is in the 100's. What a change. If you know of a site that is not listed here, please feel free to send it to the magazine. The following links have been gleaned from those recently changed or added to the US Commodore WWW Site Links page (http://www.msen.com/~brain/cbmlinks.html). To encourage these sites to strive to continually enhance their creations, and because we like to gripe :-), we'll point out an improvements that could be made at each site. #(A): Companies o http://www.escom.nl ESCOM Interactive, Inc. The new home of Commodore has links to many of its offices and some general information. The pages are still under construction, but you should probably save this address. C=Hacking gripe: No Commodore 8-bit inforation yet. o http://www.msen.com/~brain/guest/cmd/ Creative Micro Designs. Stay tuned to this site for information on the accelerator, and keep up to date on the latests prices on CMD peripherals and software. C=Hacking gripe: For a comapny wanting having just announced the Super64CPU, no mention of it is to found anywhere on the WWW site. Bummer. #(A): Publications o http://www.softdisk.com/about/c64.html LOADSTAR and LOADSTAR 128. If you are interested in LOADSTAR, check 'em out here. Some Commodore links are included, and the and a few magazine teasers are present. In addition, details on how to ordr LOADSTAR or any of its related software titles is provided. C=Hacking gripe: the background color. Yellow is hard on our eyes... Oh well. o http://www.mds.mdh.se/~dat95pkn/8bitar/ Atta Bitar (8 Bitter) magazine. Full indexes for the past 3 years, as well as information on how to subscribe. We'd tell you more, but none of us read German (At least we THINK it's German), and the English transmation page isn't done yet. Anyway, if you would like to subscribe or need to search the index of the magazine, here's the place to go. C=H gripe: Yes, we know this is English-centric, but we just wish we could actually read all the great info on this site. #(A): User's Groups o http://www.cucug.org/ Champaign-Urbana Commodore User's Group. Home of the Universtity of Illinois (the editor's alma mater!) Meeting dates and time, along with newsletters and a user group listing are presented. C=H gripe: No mention of what local CBM 8-bit users are doing. This site recently changed addresses, so change all your links... o http://www.psc.edu/~eberger/pcg/ Pittsburgh Commodore Group. Local news, meeting dates and time, and some newsletters are present. This site has also recently relocated to this new address. C=H gripe: Same as for CUCUG. We want to know what the CBM 8-bitters are doing in Pittsburgh. o http://www.slonet.org/~rtrissel/ The Central Coast Commodore User's Group. Those in the Santa Maria CA area will be glad to know that CCCUG is there for them. Past newsletter are available, and some links to other information of interest is present. C=H gripe: Meeting dates and times need to be present in some easy place. C=H plug: It sounds like this club might need a little help, as it is down on members. If you are in the Santa Maria area, consider joining... #(A): Miscellaneous o http://www.byte.com/art/9408/sec14/art1.htm Byte Magazine's Commodore obituary, by Tom Halfhill. Tom spells out many of the things that Commodore DID do right in its lifetime, and reflects on the blunders CBM made. The article makes for very good reading, but will depress some. C=H gripe: The pictures in the real article aren't reproduced in the WWW page. o http://stud1.tuwien.ac.at/~e9426444/geoswarp/index.html GEOS Warp 1.0. For the Mac user who needs or wants to run GEOS, this program, run on a Macintosh, will allow GEOS programs to operate on the Mac. The system looks very impressive, to the point of us not asking, Why? C=H gripe: Not really with the page, but the writer laments that progress is slow owing to no agreement with GEOWorks. Such things may doom the project to failure. o http://vanbc.wimsey.com/~danf/cbm/ Dan Fandrich's WWW Site. For those who develop on alternate platforms or use multiple programming languages with the C64/128, bookmark this page. Very current info, and lots of it is presented. Some defunct Commodore mags are indexed, and pointers are provided to many of the current crop of magazines, including this one. C=H gripe: the page needs a little bit more organization to make it easier to get at juicy info. o http://www.aloha.net/~scatt/commodore.htm Scatt's WWW Site. For those just moving into assembly language programming from BASIC or something else, this page has a beginner's tutorial you might find useful. C=H gripe: A little low on content, but we are glad what there is is available. o http://www.cs.wm.edu/~pbgonz/progc64.html Pete Gonzalez's WWW Site. Small page, but worth viewing. Pete shows some screen shots of a new game he is developing, and offers copies of his in progress cross assembler for the PC. C=H gripe: When's the game coming out again? :-) o http://www.ts.umu.se/~yak/cccc/ The Commodore Computer Cult Corner. Some people play games, and then some people PLAY games. Jonas Hulten has pictures of his game design, implementation, and programming heoes. You can read about each one, and even light a "candle" for them. This site has a CBM links page, which anyone can add their link to automatically. C=H gripe: We can add our home page automatically, but not our hero. o http://www.slonet.org/~jwilbur/ John Wilbur's WWW Site. Basically, just a links page right now, but we'll check back. C=H gripe: We'd like to see a little more about John as it relates to Commodore. o http://www.student.informatik.th-darmstadt.de/~supermjk/ Marc-Jano Knopp's WWW Site. Mainly a large links page for Commodore information, this site does give a glimpse of the never produced Commodore LCD laptop computer. C=H gripe: As above, we love to see a little more about Marc-Jano as it relates to Commodore. ============================================================================ #(@)zedace: Design and Implementation of an Advanced Text Editor by Craig Bruce (csbruce@ccnga.uwaterloo.ca) Note: Due to the size of the article, no executable or source code is included, but both will be included in ACE Release #15 (ftp://ccnga.uwaterloo.ca/pub/cbm/os/ace/). #(A)1: 1. INTRODUCTION This article discusses the design and implementation of the ZED text editor for the ACE operating system (Release #15 and higher). The program and full source code will be freely available when they are ready. ZED is written entirely in assembly language (ACEassembler) and makes heavy use of the full-screen control capabilities of ACE. However, part of the genius of the design of the ACE interface is that its facilities could be replicated into a standalone environment and a new ZED could be made into a one-part program (for greater convenience, with less flexibility). There was a previous version of ZED, which WAS a standalone program. It was written entirely in _machine_ language (as opposed to assembly language; y'know, hexadecimal and stuff, with a machine-language monitor). Needless to say, upgrading and maintaining the program was a real problem, even though it was only 17K in size. The program also had a couple of limitations, the most serious of which being that all lines were limited to a maximum of 80 characters (plus a carriage return), or they would be split into two physical lines internally. It would also work only on the 80-column C128. Still, the standalone version had a number of outstanding capabilities, including the ability to edit EXTREMELY large files with an REU and dynamic data structures, the ability to use "burst mode" on devices that supported it, TAB and character-set translation on loading and saving, global search and replace, range delete and recall, and a paragraph "juggling" feature. It is truly an outstanding program (if I do say so myself), and it is my pleasure to use it every day. (I also use Unix's "vi" every day, and I am getting tired of its clumsy user interface... I may just have to port ZED to the Unix environment one of these days). The ACE version has/will have all of these features and then some. The ACE version supports even larger files by using the ACE dynamic memory management system (see C= Hacking #7 or a newer ACE Programmer's Reference Guide) in addition to its own byte-oriented memory management (see C= Hacking #2) with internal memory (up to 512K on a C128), REUs up to 16 Megs, and RAMLink DACC memory up to 16 Megs. Burst-mode support isn't currently available in ACE (see C= Hacking #3), nor does the ACE version of ZED currently implement the other outstanding editing features of the original ZED mentioned above. (For another C= Hacking reference, ACE does support a three-key rollover for typing convenience (see C= Hacking #6)). However, the ACE version supports extremely long physical lines (paragraphs) by providing automatic word wrapping and "soft returns" with automatic "text sloshing" (a dynamic form of "juggling"), and it therefore has/will have the functionality of a word processor. The new version also works in all video modes that ACE supports on both the C128 and C64. #(A)2: 2. DATA STRUCTURES, FILE LOADING Now we start talking about the implementation of ZED. What a text editor is, basically, is a program that holds a text document in memory that allows you to use certain commands to alter tiny pieces or large-scale chunks of the document and then to save the changed document permanently back to disk. The way in which the document is held in memory is the content of this section. #(A)2.1: 2.1. DOCUMENT DATA STRUCTURE ZED uses a bi-directionally linked list to hold the text document in memory. A special "trailer" line is used (which is displayed with a little "house" character on it) to make modifications to the linked list and the whole list is in the form of a big ring (the links run around in circles). Here is an asciigram of a document with two data lines: /-------------------\ | /----------\ | | | V | | | +------------------------------------- | | |next|prev| data for line #1... | | +------------------------------------- | | | ^ | | V | | | +------------------------------------- | | |next|prev| data for line #2... | | +------------------------------------- | | | ^ | | V | | | +------------------------------------- | | |next|prev| special trailer line... | | +------------------------------------- | | | ^ | \----------/ | \-------------------/ I should mention that all pointers are 32-bit values, so that they can point to anywhere in ACE's "far" memory. (In fact, many of the control variables for ZED are 32 bits in size, to avoid all arbitrary restrictions on the magnitudes of various things). And, despite where the arrows point in the diagram, the value that is stored for the pointer is the address of the starting byte of the record that is being pointed to. I should also mention that lines are stored as they are displayed. If one physical line (terminated by a carriage return) has to be split (using "soft returns") over multiple display lines, then each _display_ line takes up one linked-record position in the document data structure. Using a bi-directionally (doubly) linked list (instead of a uni-directionaly (singly) linked list is a practical necessity in this environment for two reasons. First, the linking represents the natural way that the user accesses the document: he normally moves cursor up and down, page up and down. It would take a lot of time to move the cursor up using a singly linked list. Second, using a double linked list makes it easier to insert or delete a line from the document. You need to modify the previous record to point beyond the record being deleted, but the previous record is difficult to locate in a singly linked list; you must keep and manage a pointer to it (and if the user moves the cursor up, you lose what you've got for it). I prefer doubly liked lists for any job anyway, even if the ability to go backwards isn't needed, because they are easier to work with (and to construct reusable library routines for). Using a large block of memory for the data isn't really an option either. Anyone who has used SpeedScript knows what happens when you try to insert text near the beginning of a long document: it takes a lot of time to insert one space in the document memory, and the delay is annoying. Imagine this played out for a document that is N megabytes in size. (Some auxiliary data structure would be needed in this case anyway, to break the 64K barrier). I'm guessing that most other word processors/text editors for Commodore computers use this data structure (a "large" block of memory). The decision to store each display line in a single record is really one of convenience and efficiency. Most of the time, the use will be positioning the cursor and whizzing between display pages, so we don't want to be wasting any time uselessly re-formatting the text while he (sic) is doing this. We only want to re-format the text when an actual modification is made to the document. This organization does have the ugly implication that physical and logical line numbers may not always match up, but if the "target" line length (the maximum length that a single display line can be) is set to be longer than the maximum physical-line length of a file (often 80 characters), then the two will match up. Accessing the document is fairly simple. All that we need to locate the entire document is the address of the special trailer line. With this, we know directly where the bottom line is, so we can instantly go to the bottom of the document (Commodore-DOWN) and then follow the links backward to access preceeding lines. Finding the top of the document is also quite easy since the document is in a ring. We just locate the trailer line and then follow the "next" link, and we arrive at the first line (Commodore-UP). It should be no surprise that a pointer is kept to the trailer line in ZED in order to locate a document. The design allows for many documents to be held in memory at the same time, including the "kill buffer" (which is logically a complete and independent document). To make management even simpler, it should be noted that this trailer line never changes (therefore, we never have to update the pointer to the trailer line of a document). #(A)2.2: 2.2. LINE DATA STRUCTURE The format of each individual display line within a document held in the linked-list structure described above is as follows: OFF SIZ DESC --- --- ----- 0 4 pointer to the next line 4 4 pointer to the previous line 8 1 flags for the line, including $80=hard-return, $40=trailer line 9 1 number of characters on the line 10 n the displayable characters of the line n+10 - SIZE The two 32-bit pointers have already been mentioned. The "flags" field tells whether the line ends in a "hard return" or a "soft return". A hard return (indicated by the $80 bit being set) is recorded for every place in the text file where a carriage-return character is present. A soft return (indicated by the $80 bit being clear) is formatted into the document every place where a line must be broken in order to avoid it exceeding the target line length. If you modify the document, then words can be wrapped and pulled back around a soft return in order to insure that display lines are as full as possible (whereas they cannot be wrapped around a hard return). When a file is being written back to disk, lines that have a hard return flag will be written with a trailing carriage-return character, whereas lines ending with a soft return will be written with only the characters displayed on the line (no CR), and, as such, they will be logically concatenated together into the same physical line (again) in the output file. The "trailer line" flag indicates whether the current line is the special trailer line of the document or not. We need a convenient way to check for running into the trailer line, and we cannot use null pointers since the document is in a ring (note that there won't be any null pointers even if the document contains zero data lines; the trailer line will point to itself). The lower six bits of the flags field are currently unused, but they could be used, for example, to record the number of leading spaces that a line has before the first non-blank character. This would allow us to hold a file with a lot of indentation (a program, for example) using less memory per line. This feature is not currently implemented. The next field tells the number of displayable characters that are on the line and then the next field stores the actual characters in a simple string. If the line ends in a hard return, then the carriage-return character is NOT stored in the line data, since its presence is already indicated in the line header. When records are allocated in the dynamic-memory space, only the number of bytes that are actually needed are allocated. This is the number of bytes on the line plus ten bytes for the line header. Actually, the number of bytes reserved for an allocation is the number of bytes requested rounded up to the nearest multiple of eight bytes, for technical reasons discussed in C= Hacking #2. A single display line can contain up to 240 characters (not counting the CR). Every time that a line needs to be accessed, it must be fetched from far memory into a buffer in the program space. There is a slight efficiency problem in accessing a line that there isn't when allocating and storing a line. When going to read the line, you don't know how big it is, since you know nothing about it other than its far location. The conservative thing to do would be to read the first ten bytes of the line record (the header information) and then use the value in the line-length field to figure out how many more bytes you need to fetch. I kind of took a wild stab and made it so that I read the first twenty bytes of a line and then see if the line has ten or fewer displayable characters on it. If so, then I have successfully fetched the whole line and I am done in one access. (Note that there is no problem with fetching far memory beyond one record's allocation (although there certainly would be a problem with stashing)). If not, then I fetch the remaining characters and I am done in two fetches (actually, I fetch all of the characters for simplicity). However, often times I don't actually have to fetch the data of a line at all and I only need to access its header information (to follow or change its linkage, for example). In this case, I only have to access the header of the line and I only need one access to get it since I already know how long the header is (ten bytes). I can also write back a modified header in place since it is of fixed length. If I were to, for example, add characters to a line, then I would have to re-allocate a larger line record for it, free the old line-record storage, and link the new line record in with the rest of the document (by updating the previous record's "next" pointer and updating the next record's "prev" pointer and by updating any necessary global variables... quite a bit or work). #(A)2.3: 2.3. GLOBAL VARIABLES This section describes all of the global variables that ZED keeps in order to edit a document. First, I have three separate temporary-storage work areas: work1 = $02 ;(16) ;used by malloc work2 = $12 ;(16) ;used by file-load work3 = $22 ;(14) Each work area is used by successively higher levels of software in order to avoid conflicts between layers. For example, "work1" is used by the dynamic-memory routines and "work2" gets modified when loading a file. The process of loading a file involves a lot of memory-allocation work, so it is good that they use separate working storage and don't clobber each other. The following variables are used for managing the screen and the current cursor column: scrTopAddr = $30 ;(2) ;screen address of the top line on the display scrRow = $34 ;(1) ;row number of the current line on the display scrCol = $35 ;(1) ;virtual screen column number of cursor position scrRows = $36 ;(1) ;number of rows on the display scrCols = $37 ;(1) ;number of columns on the display scrStartRow: .buf 1 ;starting row on the display scrStartCol: .buf 1 ;starting column on the display scrRowInc = $38 ;(1) ;row increment for the display scrLeftMargin = $39 ;(1) ;left margin for displaying lines statusMargin: .buf 1 ;left margin of the status line on the display conColor: .buf 8 ;color palette Most of these fields are used for interfacing with ACE's direct-access full-screen-control calls. The ones that are used most often are allocated to zero-page locations (to reduce code size and to increase performance) and the others are allocated to absolute memory. ACE allows application programs to use zero-page locations $02 to $7F for their own purposes. The current displayed cursor location is stored in "scrRow" and "scrCol". "scrRow" is the current physical display row of the current document line (where display rows start at 2 since the control and separator lines take up rows 0 and 1), and "scrCol" tells the current position on the current line, from 0 up to the length of the line. Since this version of ZED features horizontal scrolling to handle really long display lines, "scrLeftMargin" is also maintained to tell what the column number of the left margin of the display is. When we refresh the screen, we will display all lines starting from this character position. Note that internally, column numbers start from 0 whereas they are numbered starting from 1 in all dialogue with the ape at the keyboard. Every time that the cursor could possibly move off the right or left edge of the screen, a check is made and if this happens, then the "scrLeftMargin" is adjusted and the screen is re-painted (effectively giving us horizontal scrolling). The following variables are used to keep track of various parameters: targetLen = $3a ;(1) ;length to display lines wrapFlag = $3b ;(1) ;$80=wrap,$40=showCR modified = $3c ;(1) ;$00=no, $ff=modified modeFlags = $3d ;(1) ;$80=insert, $40=indent statusUpdate = $3e ;(1) ;128=line,64=col,32=mod,16=ins,8=byt,4=fre,2=nm,1=msg markedLinePtr: .buf 4 ;line that is marked, NULL of none markedLineNum: .buf 4 ;line number that is marked markedCol: .buf 1 ;column of marked logical line "targetLen" is the length that ZED tries to keep wrappable lines as close to without exceeding. By default, it will be set to the physical display width of the screen, but it can be set to 240 characters by the "-l" option and it will eventually be manually settable to any value you want (10<=l<=240). The "wrapFlag" tells, first, whether word wrapping should be used ($80 set) or whether lines should just be broken at the target length regardless of whether it gets broken in the middle of a word or not ($80 clear), and second, tells whether carriage-return characters should be displayed to the user ($40 set) or not ($40 clear). (Actually, a suitable character to represent the carriage return is displayed, taken from the graphical palette of the current character set). You will normally want carriage returns displayed when you are using ZED as a word processor, and you will normally want them not displayed when using ZED as a text editor. The "modeFlags" tell, first, whether auto-insert ($80 bit set) or over-type ($80 clear) mode is in effect, and second, whether auto-indent ($40 set) or no-indent ($40 clear) mode is in effect. Insert/overtype is currently supported and auto-indent is not. Auto-indent is intended to eventually make programming easier by not requiring you to type a bunch of spaces to indent a new line of a source file that should be at the same nesting level as the previous line. The "statusUpdate" variable is used to reduce the amount of work the needs to be done in order to keep the status line at the top of the screen up to date. If any of the variables that control the values displayed on the status line change, then the corresponding bit in this variable should be set. After processing a keystroke but before waiting for the next keystroke, ZED will update all of the fields that have a '1' bit in this variable. The "msg" bit is special, since it tells whether a dialogue message is currently being displayed on the separator line (between the status line and the first document line), and if there is, then the message should be erased (overwritten by the separator characters) _after_ the user presses the next key (since it wouldn't be much fun if the user had only a couple of milliseconds to read the message). The "marked*" fields tell where the "mark" is currently set for range commands (like delete). Like in the stand-alone ZED, I will be making this version clear the mark after every modification to the file. The main reason for this is that it would be a pain in the but to keep track of the mark in some cases like when the line that is marked gets deleted or updated, and that having set-mark/do-operation clear the mark prevents the ape at the keyboard from accidentally hitting a range-destroy key and wiping out his document (he will get a "range not set" error message instead). The following variables are used to keep track of the current position in the document: linePtr = $40 ;(4) ;pointer to current line lineNum = $44 ;(4) ;number of current physical line headLinePtr = $4c ;(4) ;pointer to the special header/trailer line lineCount = $50 ;(4) ;number of display lines in buffer byteCount = $54 ;(4) ;number of bytes in buffer "linePtr" always points to the line record that the cursor is logically on. This is probably the most important global variable. This variable is needed so that we know what line to modify/etc. if the user enteres a letter/etc. "lineNum" gives the line number of the "linePtr" line, where line numbers start from 1. This needs to be maintained in order to tell the ape at the keyboard where in the document he is. These two fields are sequentially updated as the user moves from line to line in the document. "headLinePtr" is a bit of a misnomer since it actually points to the special trailer line. As explained above, it is used to find the top and the bottom of the document. "lineCount" keeps track of the total number of display lines in the current document, and "byteCount", the total number of bytes. Each carriage-return character counts as one byte. Keeping track of line and byte counts is for convenience rather than necessity. The following variables manage the "kill buffer", where text goes after it's been deleted but before it's completely discarded (a sort of purgatory): killBufHeadPtr: .buf 4 ;pointer to special header/trailer line of kill buf killLineCount: .buf 4 ;number of lines in kill buffer killByteCount: .buf 4 ;number of bytes in the kill buffer The kill buffer is maintained in exactly the same structure that the main document is: a ring of doubly linked line records. The three fields shown store the pointer to the special trailer line, the number of data lines in the kill buffer, and the number of data bytes in the kill buffer, respectively. In addition to the kill buffer, there is also a "rub buffer": RUB_BUFFER_SIZE = 50 rubBufPtr: .buf 1 rubBufSize: .buf 1 rubBuffer: .bss RUB_BUFFER_SIZE It is used to hold the fifty single characters that have most recently been deleted by using either the DEL or Commodore-DEL (Rub). I found that when using the old version of ZED, I would sometimes unintentionally delete a single character and then want it back, and I had to expend mental effort to figure out what it was. This mechanism takes the effort out of that job by maintaining a LIFO (Last In First Out) (circular) buffer controlled by the variables given above (note that "RUB_BUFFER_SIZE" is a constant which can be easily changed up to 255 in the source code). The "rubBuffer" is actually contained in the uninitialized-storage section of the program ("bss" in Unix terminology). (The ".bss" directive is not currently implemented in the ACEassembler, but I used it here as a shorthand for the equates that replace it). The Shift-Ctrl-R (rub recall) keystroke is used to recall the previous character (as if you had typed it in) and has the effect of resetting the "rubBufPtr". Then, each additional time that you type Shift-Ctrl-R in a row, the "rubBufPtr" is advanced backward to the character previous to the one just recalled. Pressing anything other than Shift-Ctrl-R resets the "rubBufPtr", so that you could recall the characters again, if you want. You can recall as few characters as you wish. Interpreting command keys is done with the following global variables: keychar = $58 ;(1) keyshift = $59 ;(1) sameKeyCount: .buf 1 sameKeyChar: .byte $00 sameKeyShift: .byte $ff ACE returns both a shift pattern and a key character code, so both are stored. The shift pattern allows us to take different actions for commands like Ctrl-R and Shift-Ctrl-R. The "same*" fields store the previous keystroke and the number of times that the exact keystroke has been made, so that slightly different actions can be taken when the same keystroke is made multiple times, such as with Shift-Ctrl-R (or maybe HOME). The following global variables are also maintained for various purposes: exitFlag: .buf 1 arg: .buf 2 temp: .buf 4 stringbuf: .bss 256 filebuf: .bss 256 tpaFreemap: .bss 256 linebuf: .bss 256 line = linebuf+headLength ;(241) headBuffer = $70 ;(10) ;buffer for holding the head of the current line headNext = $70 ;(4) ;pointer to the next line in a document headPrev = $74 ;(4) ;pointer to the prev line in a document headLineLe = $78 ;(1) ;length of the text line headFlags = $79 ;(1) ;$80=CR-end, $40=headerLine, &$3F=indent headLength = 10 ;length of the line header documentBuf: .bss 256 docbufNext = documentBuf+0 ;(4) docbufPrev = documentBuf+4 ;(4) docbufInfo = documentBuf+8 ;(23) docbufFilenameLen = documentBuf+31 ;(1) docbufFilename= documentBuf+32 ;(224) "exitFlag" is set to tell the main loop to bail out and exit back to the calling program. "arg" is used for scanning the command-line arguments. "temp" is used miscellaneously. "stringbuf" is used for miscellaneous string processing, and "filebuf" is used for miscellaneous file/string processing. "tpaFreemap" is used by the dynamic-memory-management code as a free-memory-page map for making allocations out of the application program area (or TPA, Transient Program Area). The ACE kernel doesn't dynamically allocate pages in the application space (since this cannot normally be done reliably), so a mechanism is needed inside of ZED to make use of this memory. "linebuf" is the place where the current line is fetched to/stashed from when it is being accessed or modified. "line" is the sub-field of "linebuf" where the actual line data is stored. The "head*" variables are allocated in zeropage and the record-header information from "linebuf" is copied to these variables whenever a line is fetched and copied from these variables when a line is stashed to far memory. Zero page is used for these variables since they are manipulated all of the time. Finally, "documentBuf" stores all of the information about the current main document. There is currently support for only one main document implemented, but the design includes the concept of the user being able to switch between an arbitrary number of documents held in memory at any time. 2.4. LOADING A FILE When ZED is first started up, its usual first job is to load in the document that was named on the command line (plus you can load a file at any time with Ctrl-L). ZED uses the standard ACE "open", "read", and "close" system calls to do this, although there is a bit of business that has to happen to get the data into the internal form that ZED uses. The job is split into a number of routines to make it easier to program. The main routine opens the file for reading and initializes that variables that the load routine uses. Among other things, the load routine counts up the number of display lines and physical bytes in the file and must wrap physical lines into display lines while reading. Later, this routine will perform on-the-fly translation from other file formats to PETSCII and will perform TAB expansion if requested. The main routine repeatedly calls subroutines to read a line into the line buffer, wrap it, and store it to memory. For the purpose of the following discussion, we will assume that the "target" line length is 80 characters, although it can be set to anything that you want. The Read routine copies characters from the "filebuf" to the line buffer, with the filebuf being re-filled with file-data characters as necessary in 254-byte chunks (the natural size of Commodore data sectors, for efficiency). Data bytes are copied until either a carriage-return (CR) character is encountered or we reach the 81st character (target+1). We have to check the 81st character because it may be a CR, and if we are in the normal text-editor mode, we can store a full 80 data characters on a display line, even if it ends in a CR (some editors, quite annoyingly, cannot do this). However, if the user selects the mode where CRs are visibly displayed on the screen, then we stop scanning the current display line at a maximum of 80 characters if a CR isn't encountered. After the characters of the display line have been put into the line buffer in the above step, it may be the case that the word at the end of the line has been abruptly broken in the middle. If the line ended in a CR, then this doesn't need to be checked. If the line didn't end in a CR and if the "wrap" mode is currently on, then an abruptly cut word will have to be wrapped to the next line. To do this, we scan from the end of the line back until we encounter the last space character on the line. Then, we cut the line immediately after that space, and remember where we cut it so that we can later process the overflown characters. If there is no space on the line (i.e., the first and only word on the line is longer than the target length), then we keep it as-is and end up breaking the word abruptly. Note that we break the line with a "soft return", so there is no damage done to the data by word wrapping. After the fat (if any) has been trimmed off the current display line, we want to store it into far memory, into the doubly linked ring structure discussed above. To do this, we first, set the pointer to the previous line to the address of the previous line record (we keep track of this) and set the pointer to the next line to Null (for now). Then we we allocate memory for the current line and Stash it out. But we're not done yet; we need to set the "next" pointer on the previous line record to point to our newly allocated line. This can be done by simply writing the 32-bit pointer value to the start address of the previous line (there is no need to re-fetch the previous-line contents or header). And after stashing out the line, we recall where we wrapped it (if we did) and copy the characters that were cut off to the beginning of the line buffer and we pretend that we have fetched these from the file as if in the Load Line step above. Then we go back to the Load Line step and continue. When loading is finished (after we hit End-Of-File (EOF) on the file being read), we flush the last incomplete line segment, if there was one (a file is not required to end in a CR) and then we cap things off with the special trailer line. This trailer line is allocated before we start loading the file (although I didn't mention it above), and now we set up its links so that our entire file is the nice doubly linked ring that we like so much. Now we are finished, and we return the trailer-line pointer, the number of display lines read in, and the number of physical bytes read in to whomever called us (it could be the command-line parser, the Ctrl-L (Load) command, or the Ctrl-I (Insert) command). There are three reasons why I like having this special trailer line around. (1) It allows us to not have to worry about Null pointers. For example, you will notice that while stashing a loaded line, I allocated the trailer line first so that we would always have a "previous" line to link with. (2) It allows the user to move the cursor beyond the physical end of the document to do operations like recall (Ctrl-R) a block of text. This was a problem with the original ZED; you had to go the the end of the file, press RETURN to open up a blank line, recall the text, and then delete the bogus blank line. (3) It allows a document to have zero characters in it in a consistent fashion. There is also a subtle but complicated issue dealing with dynamic memory allocation that I haven't discussed yet: what if it fails? (I.e., what if we run out of memory?). In this case, we must handle the failure gracefully, maintain the integrity of the document as best we can, and inform the user. In some cases, maintaining the integrity will involve un-doing committed changes to the document, which is a real pain in the butt, but which is still very important. It would be kind of annoying if you just made the last keystroke on five hours of editing work and the program aborted on an "out of memory" error, sending all of your work to the great bit bucket in the sky. #(A)3: 3. SCREEN DISPLAY, MOVING AROUND This section discusses the operations that have to do with maintaining the current secion of the document on the display and moving the cursor around within the document. #(A)3.1: 3.1. PAINTING THE SCREEN This is the essential operation that keeps the screen up to date with the docment in memory. ZED has a single function that does this operation, and it is called with the arguments: a pointer to the starting line to display, the starting screen line number to start painting at, and the ending screen line number to end painting at, plus one (lots of endings are 'plus one' since this allows me to use the BCC instruction). Some implied arguments include general information about the screen for use with the "aceWin*" kernel functions and the left margin for displaying lines. This subroutine simply goes through the display lines one by one, displays the line contents, and advances the line pointer. To display a single line, the line is fetched into the line buffer from far memory and the number of displayable characters is calculated according to the line length and the left-hand display margin. The displayable characters (possibly zero) are written to the screen memory and if the line isn't completely full, then the remainder of the line is filled in with space characters. The displaying is completely performed by the "aceWinPut" system call. One oddity that needs to be handled is running out of document lines before filling the screen range. In this case, each remaining screen line is cleared. Attributes (colors) are not used for this operation, since they are not needed and using them would only slow us down. They display-area color cells are initialized when ZED starts (as are the status-line and separator-line color cells) and don't change during operation. The colors come from the ACE palette (which the user can configure to his own liking). This subroutine is used to both repaint the entire screen (when necessary) and to repaint only one line or a few lines (when I can get away with this). Repainting takes time, so we want to repaint only what has changed. However, repainting isn't too slow, especially when compared to serial-line speeds, since screen updates are written directly to screen memory, although the C64's soft-80 screen is significantly slower than the other screen types since so much more processor work needs to be done just to make a single character appear. In the future, it may be useful to allow this function to abort in the middle of this operation if the user presses a key before the repainting is finished, in order to allow the user to work faster. For example, if you hold down the Page-Down keystroke, the speed that you go forward in the document is limited by how fast the screen can be repainted. If the repaint operation were abortable, then you could always go forward as fast as the key repeates, and when you get to where you are going and release the Page-Down key, the screen would repaint one final time and everything would be consistent. A flag would need to be kept to tell whether the screen is consistent or not, in order to make this work. There is also a function that displays a message in the separator line on the screen. It also needs to store the displayed message in order to allow the user to scroll through it if the screen is not wide enough to display it in its entirety. When the message is no longer needed, it is erased by overwriting it with separator characters. And, there is also a function that updates all of the fields that have changed on the status line. #(A)3.2: 3.2. MAIN CONTROL Before I start talking about the implementations of the individual commands, I should say something about the main control for the program. After ZED initializes and loads the initial document (even if you don't specify one, ZED will default to the name "noname" and try to load it), control is passed to a small main loop of simply displaying the cursor, waiting for a keystroke, undisplaying the cursor, calling the subroutine associated with the keystroke, and repeating. #(A)3.3: 3.3. END UP, DOWN, LEFT, RIGHT Moving the current line to the top and bottom of the document is straight forward, because of the organization that was discussed in the data- structure secion above. To go to the top of the document, copy the trailer-line pointer to the current line pointer and then fetch the next pointer from the trailer line's header; this will give a pointer to the top line. Then we set the current line number to one and a couple of other variables and call the subroutine discussed above to repaint the screen. Going to the bottom of the document would be just as easy as going to the top, except that we want the last line (the trailer line) to be displayed on the bottom of the screen in order to present as much useful document content to the user as possible. If there are fewer lines in the file than will fill a screen, then we cannot, of course, display an entire screen. To make this business easier, a subroutine is provided that, given a starting line pointer and a count, will scan until it either hits the count-th line previous to the given one or it hits the top line of the document. It returns the number of lines that were actually scanned upwards (possibly zero), the pointer to the line that it stopped scanning at, and a flag indicating whether it stopped because it hit the top of the document or not. This subroutine is quite generally useful. There is a similar subroutine that scans downward. So, after locating the bottom line of the document and setting the line number, the scan-upwards subroutine is called to scan upwards the number of displayable lines on the screen. The screen is then repainted in its entirety from the line that was scanned up to, and the new cursor-display location is computed from the count of the lines that were scanned over. This works equally well for a long document, a document shorter than the height of the screen, and an empty document. The End-Left and End-Right commands are very simple in that they don't even have to change the current line pointer, but they do have to check if the cursor has moved off either the left or right edge (margin) of the screen. The visible columns go from the column number of the left margin, up to that plus the width of the screen. If the cursor goes off an edge of the screen, then the new left margin will have to be computed and the entire display will need to be repainted. This repainting effectively achieves horizontal scrolling. Lines in ZED, of course, can be up to 240 characters across (241 if you count the carriage- return character), but the widest screen that ACE supports is 80 columns. Arguably, the horizontal scrolling could be done more efficiently by moving the contents of the display left or right by the requred number of columns and then filling in the opened spaces with the data from the correct columns of the display lines in memory. However, the additional complexity is non-trivial and the speedup may not be all that great except for the soft-80 screen of the C64. A better approach might be to go with the interruptable- repainting idea that I spoke of earlier, if the current line were updated first (so that you can see what you're doing) and the rest of the lines afterwards. #(A)3.4: 3.4. PAGE UP, DOWN, CURSOR UP, DOWN, LEFT, RIGHT, WORD LEFT & RIGHT All of these functions follow quite naturally from what is above. For Page- Up and Down, the scan-up or scan-down subroutines already described are called to find the new current line for the cursor and then the entire screen is repainted, effectively paging up or down. For cursor up and down, we just go up or down to the next line in the document and adjust adjust the cursor location on the screen. If the cursor goes off the top or bottom of the screen, then we scroll the screen up or down as appropriate (ACE can scroll the screen up or down and will eventually be able to scroll it left and right (although this will be a bit painful for the soft-80 screen since it may mean scrolling left and right nybbles)). Then, we display the current line at either the top or bottom of the screen to fill in the blank line that we just opened up. Because we use scrolling and painting only a single line, we can scroll the screen fairly quickly, easily keeping up with the cursor repeat rate, except on the soft-80 screen. For cursor left and right, we advance the cursor one position on the line and see if it has gone over the edge. If not, then we are done; nothing needs to be redisplayed. If we have gone off the edge, then we call the cursor-up or cursor-down routines to go to the previous/next line and we position the cursor to either the end or start of the new line. The word left/right functions are similar to the cursor left/right functions, except that we keep scanning until we run into the start of the next word. For word left, this is defined as running into a non-whitespace character that is preceeded by a whitespace character. A whitespace character is defined as either a space, a TAB, a hard return, or the beginning or ending of the document. For word right, the start of the next word is defined as a non-whitespace character that is preceeded by a whitespace character, where we start searching from one position to the right of the current cursor position. If we run into the beginning or end of the document, then we stop there. BTW, all of these moving-around functions check the cursor position against the display bounds and "scroll" the display left or right if the cursor has gone off the screen. Well, actually, there is one exception to this rule. If the current line is the target-length number of characters long, and the target length equals the screen width, and carriage returns are selected not to be displayed, and the left margin of the display is column one (external), and the cursor is in the target-length-plus-one position of the line, then the screen is NOT scrolled right. Instead, the cursor is displayed on the last position of the line and is made to blink fast (an ACE feature). This is done to avoid the annoyance of having the screen scroll right when you are editing a text file on, say, an 80-column screen that has up to 80-character lines in it. The standalone ACE does this too, when you logically hit the 81st column. #(A)4: 4. TEXT INPUT AND "SLOSHING" So far, we can load up a document and whiz around inside of it, but we can't actually change anything. This section describes the single-character modification operations of character input, rub, and delete, and the text "sloshing" algorithm that is needed to make sure that lines are always as full as they can be without going over the target length ("Come on down!"). #(A)4.1: 4.1. TEXT INPUT, DELETION Adding a single character to a document isn't really very difficult. There are two modes for single-character inputting: insert and overtype. Insert mode is generally more useful and more often used, but overtyping can be very useful when dealing with tabular or specially formatted text. Therefore, we must support both modes. In some other text editors, overtype mode is the natural mode because the cost of inserting a character can be so high, but not here. Actually, there isn't a whole lot of difference in the implementations of the two modes. For overtype mode, you just fetch the current line, take the inputted character and store it into the line buffer at the current position, stash the line back into memory, and repaint the line. For insert mode, we do the same thing, except that we copy the line from the cursor to the end to one position beyond the cursor (backwards) and bump its length up by one before storing the new character on the line. Rubbing out a character (Commodore-DEL) is done in quite the same way, except that we copy the rest of the line back one space and decrement the length. Oh, and when the length of the new line is different from the length of the old line, we have to deallocate the old line and allocate new memory for the new line, and surgically link it in with the rest of the document. I have a subroutine that does this. The DEL key is handled as if you had typed Cursor Left then RUB, except when you press DEL on the first column of a line and the previous line ends with a hard return, the hard return of the previous line is removed instead. I decided to make the RUB (Co-DEL) key return an error instead of joining lines together like DEL, because sometimes it is convenient to just lean on the RUB key to delete to the end of a line. #(A)4.2: 4.2. TEXT SLOSHING But, we're not done with text modifications yet. If we just left the modifications as described in the previous sections, we would be end up with lines that are longer than the target length, with ragged lines, and with lines that don't join together like they should after pressing DEL in the first column. After each of the modifications, the text-sloshing routine is called to straighten everything up and figure out how to redisplay the screen. Often, only one line needs to change, but sometimes, many lines or even the whole screen will have to be updated, as sloshing text can continue for many lines as line overflows and underflows cascade forward from the line that has just been modified. In fact, the text-sloshing routine is enormously complicated and has many, many special cases. (Although, for all of its complexity, it is only 400 lines long, although it still needs a few more features). There are many more cases that could cause sloshing than you might think. There are the obvious inserting/deleting one-too-many characters in the current line, but there is also the case that an insertion or deletion causes a space character to move to the right position on a line to allow the line to be sloshed backward, or maybe you remove a space from the end of a line that creates a word that would be too long to be contained on one line and therefore needs to be sloshed forward. The sloshing algorithm doesn't introduce or take away any characters from the body of the document; it just reorganizes the existing characters. All of the spaces are retained at the ends of wrapped lines. We don't want to delete spaces, since it is difficult to reconstruct them, since two spaces are normally between two sentences in text, but it is difficult for a computer to figure out where a sentence ends. In fact, keeping these spaces can sometimes cause an anomaly: if all of the spaces won't fit on the end of one line, then they will be displayed at the beginning of the next line. The variables that are maintained by the algorithm are as follows: sloshLinesAltered = work2+0 ;(1) ;a simple count sloshRedisplayAll = work2+1 ;(1) ;$80=redisplay to bottom, $ff=force all sloshRedisplayPrev = work2+2 ;(1) ;whether previous line needs to be repainted sloshMaxChars = work2+3 ;(1) ;number of chars that can be sloshed sloshTailChar = work2+4 ;(1) ;the last char of prev line sloshTheCr = work2+5 ;(1) ;whether a CR should be sloshed sloshLinesInserted = work2+6 ;(1) ;number of new line records created sloshLinesDeleted = work2+7 ;(1) ;number of existing line records deleted sloshCurAltered = work2+8 ;(1) ;whether the current line has been altered sloshTerminate = work2+9 ;(1) ;flag to terminate (hit a hard return) sloshCurTailChar = work2+10 ;(1);last char of current line The algorithm has a main loop that is repeated for each line that can be sloshed. The loop exits when we either run into a hard return or we run into a line that does not need to be sloshed. We start scanning from the cursor line of the main document. We first look at the previous line and see how many more characters it can accommodate before being full. Then, we scan the current line from this point (the number of characters that could potentially be sloshed backwards) back to the start of the line searching for spaces. If there are no spaces, then the current line cannot be sloshed back onto the previous line. If we do run into a space, then we stop searching and know that we can (and must) slosh back the current line. So, we remove the characters from the current line and write it back (maintaining links as appropriate) and then go back to the previous line and append these characters to it. If the cursor happened to be on the line in a position that got sloshed back, then we must adjust the cursor position and move it back to the previous line. If the previous line is before the start of the screen, we must set the flag to redisplay the entire screen later. If the current line is the first line of the slosh area but the cursor didn't get moved back to the previous line, then we must set the flag to indicate that we must redisplay the previous line too when we repaint the screen. If it turns out that we have sloshed back ALL of the characters on the current line, then we must remove the empty line record of the current line from the document and adjust the global line count. We also have to worry about sloshing back a hard return, and if we do, then we bail out of the algorithm since we are done. Oh, and we have to keep in mind whether we are displaying carriage returns or not in calculating line lengths. After shoshing backward, we check if the current line needs to be sloshed forward. It needs to be sloshed forward if it is either longer than the target length or it ends in a non-space character and the first word on the next line cannot be sloshed back. The latter is a special case and needs to be checked for specially, even thought the functionality for doing so is redundant. If we do need to slosh forward, we start scanning the current line at the target-line-length point and scan backwards until we hit the first space. If there is no space, then the current word is longer than the target length and must remain abruptly broken over two (or more) lines. We wrap it at the target length. If we do find a space earlier on the line, then we wrap the line right after that space. Oh, I forgot to mention: we need to do something special for lines that end with non-spaces for backward sloshing too. If the previous line ends in a non-space (presumably because it contains a single very long word), then we don't find any spaces to slosh back to the end of the word, then we slosh back as many characters as will fit, since the word is broken anyway, and we want it to have as many characters as possible on a single line. To wrap the line, we set the length of the current line to the new length and replace the old version of the line in memory. Then, we create a new line record and store the characters that were wrapped in it and link this line record in with the rest of the document. And this is all that we do here; we don't actually insert the wrapped characters into the next line, since that would be more complicated, and since it might cause that line to overflow. If we just leave the wrapped characters, they will be joined with the next line (if necessary) on the next iteration of the main sloshing loop in a slosh-back operation. We must adjust the cursor location, like before, if the cursor was on the part of the line that got wrapped around. At the end of a loop, we check to see if we have passed a hard return in the document, in which case, we exit. Otherwise, if either the current line has been modified or if the current line is the first line to be sloshed, then we go on to the next line and repeat the above procedure. On our way out, we do a little fine tuning of the "sloshLinesAltered", "sloshRedisplayAll", and "sloshRedisplayPrev" variables, which were described earlier. These variables will be used to repaint the changed portions of the screen display. Part of the fine adjustment includes comparing the number of lines that have been inserted and deleted from the document. If these two numbers match, then the bottom portion of the screen doesn't have to be repainted, only the altered lines themselves; otherwise, we need to repaint from the current line all the way to the bottom of the screen. The sloshing algorithm currently does not handle non-wrap mode; lines will always be word wrapped. Later, all lines will be broken at the N-th column if you are not in word-wrap mode. Also, the algorithm can produce an anomalous wrapping in one case involving lines that end with non-spaces that I can't seem to remember that this algorithm will fail in (but, the document will still be interally consistent). And finally, if you change the target line length while editing a document (which you can't currently do), then the algorithm may not be able to give optimal word wrapping in all cases (though, again, the document will always be interally consistent). #(A)5: 5. OTHER FEATURES This secion discusses the other features of the editor that have not been described yet. In general, the operation of this version follows closely from the standalone version, so you can read its documentation for more details. First, I will give a summary of all of the implemented and planned commands, and then I will discuss the operation of a few selected commands. #(A)5.1: 5.1. COMMAND SUMMARY Here is a command summary. The "I" column in this list tells whether the feature is currently implemented or not. A blank means "no", and an asterisk means "yes". Note that "currently" means "by the time that you read this" (which will be a couple of weeks after I have written this). The "CODE" column tells the internal ACE-PETSCII code for the key. A plus symbol following it means that the shift status of the key is checked to distinguish this key. The "KEY" column tells what keystroke you must make ("CT-" means Ctrl, "CO-" means Commodore, "SH-" means Shift, and "AL-" means Alt). The "ACTION" column tells you what happens. I CODE KEY ACTION - ---- ------- ------- $e0 CT-@ Exchange cursor position with mark position * $e1 CT-A Alter case of letter under cursor $e2 CT-B Go on to next document buffer $e2+ SH-CT-B Go on to previous document buffer $e3 CT-C Copy range * $e4 CT-D Delete range * $e5 CT-E Exit with save * $e6 CT-F Find next occurrence of hunt string $e6+ SH-CT-F Find previous occurrence of hunt string $e7 CT-G Go to given line number $e7+ SH-CT-G Go to given _physical_ line number * $e8 CT-H Set Hunt string * $e9 CT-I Insert new file into current one $ea CT-J Juggle the lines of paragraphs, keep separate $eb CT-K Kill current line * $ec CT-L Load file $ed CT-M Set Mark for range operations * $ee CT-N Set Name of current file $ef CT-O Set Options: input/output translation/tab-expansion, etc. $f0 CT-P Print current file * $f1 CT-Q Quit without save * $f2 CT-R Recall text from the Kill buffer * $f2+ SH-CT-R Recall text from the Rub buffer * $f3 CT-S Save file $f4 CT-T Tie together multiple lines into one big line (paragraph) $f5 CT-U Undo the last change made to the document $f6 CT-V Verify file $f7 CT-W Write range with new name $f8 CT-X Extract the individual lines from a paragraph $f9 CT-Y Replace (all the other letters were taken!) * $fa CT-Z Goto bottom of screen * $fb CT-[ Toggle insert mode * $fc CT-\ Toggle modified flag * $fd CT-] Toggle indent mode $fe CT-^ Change the current working directory $ff CT-_ Compose ISO-8859-1 character I CODE KEY ACTION - ---- --- ------ * $91 UP Cursor up * $11 DOWN Cursor down * $9d LEFT Cursor left * $1d RIGHT Cursor right * $06 SH-LEFT Word left * $0b SH-RIGHT Word right * $16 CT-UP Page up * $17 CT-DOWN Page down * $19 CT-LEFT Page left * $1a CT-RIGHT Page right * $0c CO-UP Goto top of document * $0f CO-DOWN Goto bottom of document * $10 CO-LEFT Goto beginning of line * $15 CO-RIGHT Goto end of line * $0d RETURN Split current line (indent not yet implemented) $8d SH-RETURN Go to next paragraph $01 CT-RETURN Go up one paragraph $09 TAB Tab $02 SH-TAB Backtab $18 CT-TAB Insert to next tab stop * $14 DEL Delete character * $08 CO-DEL Rubout * $94 INST Insert one space * $13 HOME <nothing> * $93 CLR Cursor home $04 HELP Bring up help window $84 SH-HELP Display help screen * $0a LINEFEED <nothing> * $07 SH-LINEFEED <nothing> * $1b ESCAPE Redisplay screen * $0e SH-ESCAPE <nothing> * $03 STOP <stop some operations> * $83 RUN <nothing> $90 CT-1 Clear document $05 CT-2 Clear buffer $1c CT-3 Enter hexadecimal PETSCII character code $9f CT-4 Display directory $9c CT-5 Destroy current document buffer $1e CT-6 Create new document buffer $1f CT-7 Display PETSCII code of current character * $9e CT-8 Scroll left margin of status line * $12 CT-9 Reverse screen on * $92 CT-0 Screen reverse off $81 CO-1 Set display to show single buffer $95 CO-2 Set display to show two buffers $96 CO-3 Set display to show three buffers $97 CO-4 Set display to 40 columns, default rows $98 CO-5 Set display to take full screen $99 CO-6 Set display to default number of rows $9a CO-7 Set display to maximum number of rows $9b CO-8 Set display to 80 columns, default rows $85 F1 Function key 1 : user-defined string $89 SH-F1 Function key 2 : user-defined string $86 F3 Function key 3 : user-defined string $8a SH-F3 Function key 4 : user-defined string $87 F5 Function key 5 : user-defined string $8b SH-F5 Function key 6 : user-defined string $88 F7 Function key 7 : user-defined string $8c SH-F7 Function key 8 : user-defined string $80 CT-F1 Function key 9 : user-defined string $82 CT-F3 Function key 10 : user-defined string $8e CT-F5 Function key 11 : user-defined string $8f CT-F7 Function key 12 : user-defined string #(A)5.2: 5.2. TEXT SAVE This function is, of course, implemented, since the text-modification functions of the editor would be useless without it. It is really quite simple, because of the data structure of the document. First, we try to open the file for writing. If not successful and we get a "file exists" error, then we scratch the old file. Then, we re-open for writing. To save the file contents, we start at the top line, and fetch each line in turn until we hit the trailer line of the document, at which point we are finished. After fetching the line, we check if it ends with a hard return, and if so, we append the line buffer with a carriage return character and bump up the line length. We then call the ACE "write" primitive with the line buffer as the argument to write out the line. Writing in this size of chunk rather than in single bytes gives ACE the opportunity to carry out this operation as efficiently as it can. We then close the file and we are done. We display status information to the user during all phases of this operation, and we certainly tell him if anything goes wrong. #(A)5.3: 5.3. RANGE DELETION & RECALL Range delete and recall are implemented, since they are very useful for general editing. What will normally happen is that the user will set the mark with Ctrl-M (mark) to one end of the range to be deleted, and then move the cursor to the other end of the range and press Ctrl-D (delete). The text then disappears into the kill buffer and can be recalled any number of times at any point in the document using the Ctrl-R (recall). One difference between this ZED and the operations mentioned here are "character oriented" rather than "line oriented". So, you can now delete only portions of lines rather than entire lines. You just have to keep in mind that the cursor is logically located "between" the previous character and the character that the cursor is currently over. For example, if the cursor was on the "y" in "xyz", then the mark would be set to between the "x" and "y" if you pressed Ctrl-M at that point. This also means that if you wanted to delete an entire line (that ended with a hard return), then you would move the cursor to the first character of the line and press Ctrl-M and then move the cursor to the first character of the NEXT line and press Ctrl-D. (The Hard Return itself won't be included in the delete operation if you move the cursor to the end of the line to be deleted--this is one of the reasons for having a displayable trailer line). To implement the delete operation, all of the lines in the operation are unlinked from the main document and are linked into the kill buffer. If there already was something in the kill buffer, then it is deallocated and the kill buffer is cleared. A trailer line is permanently allocated to the kill buffer, to make it work consistently with the main document. Partial lines (potentially, the first and last lines of the range) are a bit of a pain and have to be split into two lines at the point of the mark/cursor, where one of the broken lines stays with the document and the other goes into the kill buffer. After extracting the range, the lines around the extracted region are sewn back together ("sponge, nurse!") and text is "sloshed" about the stitch point (if necessary). The number of bytes and lines involved are counted up and are subtracted from the global counts for the main document. Ctrl-C (copy) is very similar to the delete operation, except that the data to be deleted is actually copied to the kill buffer and the document is left unmodified. Range copy is not currently implemented, since its operation can be emulated with a Ctrl-D followed immediately by a Ctrl-R. To implement the recall operation, the kill buffer is replicated into a temporary document and the current line of the main document is broken into two lines at the recall point (if necessary). Then, the temporary document is linked into the main document at the recall point and the text is "sloshed" about the two stitch points. The line and byte counts are adjusted, and we are done. #(A)5.4: 5.4. TEXT SEARCHING Forward text searching is implemented, since it is very useful for both finding things and for moving quickly around in a document. (Reverse search and Range search and replace are not currently implemented, since they are less useful). The implementation is quite straightforward. The user will first use Ctrl-H (hunt-string) to set the string to search for. The user will input this on the top line of the screen, and we don't have to do much work for inputting the string, since ACE already provides a console-input routine complete with left/right cursor movement and a scroll-back buffer (although it is a bit hard to use if the input line is longer than the input window on the screen). After the search string is set, the user will press Ctrl-F (find) to find the next occurrence of the string. So, we just search for that string, starting at the cursor position to the right of the current position. A simple algorithm of keeping a pointer to the current scan position in the hunt string and pointers to both the current position in the document and to the position in the document corresponding to the start of the string is used. If the current document character matches the current hunt-string character, then both the document and hunt-string pointers are advanced. If the hunt string is exhausted by this, then we have found a match and can stop searching. We move the cursor to the saved document position of the start of the hunt string and exit. If the characters don't match, then we move the current document pointer back to the postition corresponding to the start of the hunt string, advance it by one, save it, and start searching again. Our algorithm needs to be able to wrap around soft returns in the main document. #(A)6: 6. CONCLUSION So, here we finally have the basic ACE version of the ZED text editor that I have been promising for a very long time. The new version doesn't contain all of the features of the standalone version, but I am working on it. The new version does, however, include a few features that the old version does not, like long lines, horizontal scrolling, text "sloshing", the ability to use additional memory types for storage, the ability to work on the C64, integration with a command-line environment, and full assembler-code availability (Real Soon Now(TM)). In order to make ZED operational as a word processor, some means of giving embedded commands and for formatting and printing these commands must be provided. I was originally thinking that an EasyScript or SpeedScript kind of embedded-command structure, and then I was thinking abouta LaTeX kind of structure (the LaTeX structure is superior), but I am now thinking that an HTML type of format-command structure might be rather apropos. Why then I would need to create a print formatter and previewer that might be usable for other purposes, too. ============================================================================ #(@)trivia: Commodore Trivia by Jim Brain (brain@mail.msen.com) #(A): Introduction Well, the cold has moved in on us in Michigan, but the Commodore information coming into the house is keeping us warm. Some orphan computers have showed up, including a Commodore 65 and C116, as well as a couple of Commodore B-128 computers with all the fixin's. So, armed with the hardware, I have come up with some brain ticklers for the Commodore know-it-all. As some may know, these questions are part of a contest held each month on the Internet, in which the winner receives a donated prize. I encourage those who can received the newest editions of trivia to enter the contest. This article contains the questions and answers for trivia editions #19-22, with questions for the current contest, #23. If you wish, you can subscribe to the trivia mailing list and receive the newest editions of the trivia via Internet email. To add your name to the list, please mail a message: To: brain@mail.msen.com Subject: MAILSERV Body: subscribe trivia Firstname Lastname help quit #(A): Trivia Questions Q $120) What is the model number of the assembler/monitor for the KIM-1? A $120) The KIM-5 was the model number of the editor/assembler product. Q $121) How many LEDs are on the KIM-1? A $121) The basic unit contains 6 7-segment LED displays, or 42 LEDs if you count each LED in a segment. Q $122) What is the model number of the REC chip used in the REU? A $122) MOS 8726. Q $123) At least two versions of the above chip exist. What is the main physical difference between the versions? A $123) The eraly versio of the chip (8726-R1) exists in DIP form, while the 8726-r4-r8 exists as a "J-lead" square surface mount unit. Q $124) Why couldn't regular Atari(tm) style joystcks be used with the Commodore Plus/4 series? A $124) Instead of using the de-facto 9 pin D-subminuture connector for the joysticks, the Plus/4 series used small mini-DIN connectors. Some sources claim the older connectors were leaking a fair bit of radio interference and were preventing the units from attaining FCC approval, so the connectors were changed to the better-shielded mini-DIN types. Q $125) What was the first joystick model Commodore produced that would function with the Plus/4 computer line? A $125) The Commodore T-1341 Joystick, which had the special mini-DIN connector Q $126) How many computer models are included in the Plus/4 line? A $126) At last count, 3 models in the Plus/4 series were produced: The Commodore Plus/4 The Commodore 16 The Commodore 116 Some Commodore 264 models are known to exist, but are not counted, since the 264 was the prototype model of the Plus/4. Also, a V364 model was planned, but only one unit is known to exist. Q $127) In a normal Commodore disk drive Directory Entry, what relative offset denotes the start of the program name? A $127) The filename starts at the 4th byte in the directory entry. Q $128) How many tracks in a 1541 or 4040 are normally available for use as storage? A $128) 35 tracks. Q $129) How many bytes comprise a single disk drive directory entry? A $129) 30 bytes. Q $12A) What is the model number of the Commodore dual drive with a total capacity per unit of 2.12MB? A $12A) The Commodore 8250 or 8250LP dual disk drive. Q $12B) On the drive denoted in $12A, how large could a single sequential file be? A $12B) 1.025 megabytes. Q $12C) At least two version of the Commodore 64C keyboard exist. What is the difference between them? Extra Credit: Why? A $12C) One one keyboard style, the Commodore graphics are printed on the front of the keys, while they appear above the letters on the keys in the second type of keyboard. I can't answer the extra credit part except to say that Commodore was always seeking the best deal. aybe a new keyboard manufacturer got the bid and changed the layout. Q $12D) On the Commodore 64, what area of memory is swapped out when using an REU with RamDos? A $12D) $6000 - $7fff is swapped out when a RAMDOS command is executing. Q $12E) Commodore manufactured two different versions of the 1541-II drive. What is the difference between them? A $12E) The drive mechanisms differ in the two drives. You can tell which you have by the appearance of the front of the drive. If the lever hits a rest in the release position, you have the direct drive model. If the lever has no such rest visible, the drive cotains the belt drive mechanism. Q $12F) How many colors could the Commodore 1520 plotter plot in? A $12F) 4. red, black, blue, and green. Q $130) The Commodore Plus/4 was referred to as the "___________ Machine". A $130) Productivity. Q $131) Although the Commodore 16 and 116 were functionally equivalent, what two physical characteristics distinguished one from another? A $131) Case style and keyboard. The C16 is enclosed in a VIC20/C64 style case with keyboard, while the C116 sports a scaled down Plus/4 style case and "chicklet" keyboard. Q $132) How many pins are there on the Commodore plus/4 expansion port connector? A $132) 50 pins. Q $133) On which side of the Commodore 65 (as it is facing you) did Commodore place the power switch on? A $133) The left side. Since the disk drive fills the entire right side, the left side is an obvious choice, as the swith would require cabling if installed on the right side. Q $134) How many keys are on a standard Commodore 128 keyboard? A $134) 92 keys. Q $135) What color are the drive LEDs on the SX64 drive? A $135) There is only one LED, a red in-use LED. Q $136) True or False? The Commodore 64 and VIC-20 keyboards are interchangeable. A $136) True. Q $137) On a 1526/MPS 802 printer, how many redefinable characters were available for use per line of text? A $137) 1. True fact: In order to print a line of graphics, one must print a GFX char, do a returb without linefeed (resets the graphic character, evidently), the tab over and repeat the cycle until 80 characters were printed. I had one, and it took me 7 hours to print 21 pages of GEOWrite text! Q $138) To set up a redefinable character on the MPS 802/1526 printer, what secondary address must be opened? A $138) Secondary address #5. Q $139) How many pins are in each Euro-DIN plug used on the Plus/4-C16 joysticks? A $139) 8 pins. Q $13A) How many pins are on a regular Commodore VIC-20/C64 joystick connector? A $13A) 9 pins. Q $13B) What BASIC command is used to change from C128 mode to C64 mode on a C128? A $13B) go 64. It will ask for confirmation. Q $13C) What were the four integrated programs included in the infamous "3+1" software in the Plus/4? A $13C) A word processor, spreadsheet, graphics software, and a data management program. Q $13D) Which Commodore serial printer(s) had a small switch that allowed it to be addressed as either device 4 or device 5? A $13D) The 1525, MPS 801, and MPS 803 had such a switch. Although I cannot confirm this, I believe the 1515, the precursor to the 1525, also had the 4/5 switch. Q $13E) How many addressable registers does the Commodore VIC-II IC have? A $13E) There are 47 control registers in the Commodore VIC-II chip. Q $13F) On a Commodore PET machine, what output appears on the screen after typing in SAVE "",2? A $13F) PRESS PLAY AND RECORD ON TAPE #2 Q $140) What was the model number of the microprocessor used in the first of the Commodore 264 Series? A $140) The early Plus/4 units contained a 7501 microprocessor, and the later units featured a 8501 microprocessor. The only differences between the two units is the manufacturing process and die size. Q $141) How fast could the microprocessor in the Commodore 264 Series theoretically run at? A $141) 1.76 MHz. Q $142) How many colors can a Commodore Plus/4 display at once? A $142) 8 shades each of 16 colors, but the 8 shades of black are still still black, so a total of 121 colors are possible. Q $143) What anomaly exists in the numbering of the BASIC interpreter in the Plus/4 as 3.5? A $143) This version contained almost all of the commands in Version 4.0, plus some new commands for graphics and sound. Q $144) After the very first 1581 disk drives were introduced, Commodore found that the WD1770 disk controller chip in the drive could corrupt the disk in some situations. So, Commodore offered a replacement IC to fix the problem. What was the number of the replacement IC? A $144) The Western Digital WD1772 IC. Q $145) On some very early CBM 1541 drives, what would happen if the serial bus CLOCK and DATA lines were high upon startup? A $145) On the very first 1541 drives (I suspect the feature was also on the 1540 as well), On power-up, the drive would jump to a subroutine at $E780 after performing the reset routine. The code there would check for the high state of CLOCK and DATA. If found, the code would wait until both go low and then store '*' into the filename buffer, sets the filename length to 1, and then jumps to the & command, which loads a USR file and executes it. Since the Commodore computer never used this feature, and some machines would boot with these lines randomly high, Commodore removed the feature. Q $146) In question $0F8, we learned that one must DIMension an array in BASIC if it will have more than 11 elements. Which Commodore produced reference book ncorrectly claims the need to DIMension arrays for more than 10 elements. A $146) The Commodore 128 Programmer's Reference Guide. Page 17. Q $147) Why should serial device number 31 not be used? A $147) While it is specified as a valid serial bus address, when "or"ed with certain commands, it results in a bad command, hanging the bus and the serial drivers. Q $148) On most VIC game cartridges from VIC-1910 up, toggling interlaced screen display can be done with a keypress. Which key? A $148) Press the F7 function key. Q $149) Which cartidge fitting the criteria in $148 does not toggle interlace display with the same keypress as the others? How is it toggled on this cartridge? A $149) Gorf, VIC-1923. Pushing the joystick up toggles interlace mode. Q $14A) The Commodore 64 KERNAL and BASIC code use every opcode in the 6510 CPU except three. Which three? A $14A) BRK, CLV, and SED. Q $14B) For what purpose does the BASIC interpreter in a Commodore 64 require the Complex Interface Adaptor (CIA) IC? A $14B) In order to calculate random values for the BASIC function RND(0), the first 4 registers of the CIA whose address is provided by the IOBASE KERNAL routine are read. Q $14C) On the Commodore 128, the 80 column output is output by the VDC chip. What does VDC stand for? A $14C) Video Display Controller. Q $14D) By now, most people know about the ill-fated Commodore 65. What were the specifications on the original Commodore 65 idea? A $14D) A Commodore C64C with a built-in 1581. Q $14E) When referring to the Commodore 4032, one usually states that one has a "thin 40" or a "fat 40". What does "thin" and "fat" signify? A $14E) A "thin 40" had a 9" screen and could not be upgraded. The "fat 40" had a 12" screen, and could be upgraded to a 8000 series machine with some upgrade chips. Q $14F) If you own a Commodore 4032, how can you tell which kind (thin or fat) you have? A $14F) If you hold down the cursor key and it repeats, you have a "fat 40". (Of course, inspection could also be used, as the "thin" unit had a smaller screen) Q $150) How nmany keys are on a standard Commodore B-128 keyboard? A $150) 94 keys. Q $151) How many revisions of the 1541 printed circuit board are known to exist? A $151) For the 1541: PCB# 1540001 The "long board", as used in the 1540. PCB# 1540008-01 Minor revisions to the 1540001 board. PCB# 1540048 The "short board". PCB# 1540050 Minor revisions to the 1540048 board. -01 ALPS mechanism -03 Newtronics mechanism PCB# 250442-01 A revision of the short board. 1541 A board PCB# 250446-01 Minor revisions to the #250442 board, 1541 A-2 board PCB# 250446-03 Cost reduced 250442-03 board. the 1541A C/R. For the 1541C: PCB# 250448-01 Contains the track 1 sensor logic. the 1541B board. For the 1541-II: PCB# 340503 Cost reduced board. Termed the 1541-II board. There might be others, but these we can confirm. There are 9 if you count the 1541-II board as a 1541 board, 8 if not. Q $152) The Commodore 6510 CPU has two internal I/O registers. Where in the Commodore 64 are these two registers located at? A $152) Location $0000 and $0001 Q $153) The Commodore 64 cotains 64kB of memory. How many bytes is in 64kB? A $153) 65536 bytes Q $154) What is the name of the Commodore employee responsible for much of the Commodore 128 and 65 software development, among other accomplishments? (hint: initials are FB) A $154) Fred Bowen. Q $155) In question $13F, we found out the message that was displayed after typing SAVE "",2. Why did Commodore change that message on the VIC-20? A $155) The original message, as detailed in Q $13F was: PRESS PLAY AND RECORD ON TAPE #2 Commodore found that people were pressing the play buttopn BEFORE the record button, which would prevent the record button from functioning in some cases. So, Commodore changed the message to: PRESS RECORD AND PLAY ON TAPE To circumvent the problem. Note that the VIC did not have 2 tape interfaces, so no cassette number was needed. Q $156) What was the number of Commodore 64 machines sold, within 4 million? A $156) 17 million (This information came from Dave Haynie) Q $157) What was the number of Commodore 128 machines sold, within 1 million? A $157) 4.5 million (This information came from Dave Haynie) Q $158) In 1985, Commodore previewed the Commodore LCD Laptop computer at the January CES show. How many software packages were to be built-in? A $158) 8: Word Processor File Manager Spreadsheet Address Book Scheduler Calculator Memo Pad Telecommunications Package Q $159) In the Commodore LCD unit, what were the text screen dimensions? A $159) 80 coumns by 16 rows. 1200 characters on screen. Q $15A) What is the version number of the only known "bug-free" VIC-II IC? A $15A) 6569-R5. What's funny is that this chip was manufactured after the Commodore 128 was introduced, so they used the 6569-R3 for the development of the Vic-IIe chip (8563 series), which is buggy. So, the newest PAL 64s have a better VIC than the C128. Q $15B) Machine language programmer typically use the .X register to index into small arrays. What is the largest byte-array size that can be handled in this way? A $15B) 256 bytes. Q $15C) In the mid-1980's, Commodore started manufacturing IBM clone PCs. One of the models had a name which was a type of horse. Name the term. A $15C) The Commodore "Colt" PC. Q $15D) What is the model number of the first mouse introduced for the Commodore 64? A $15D) The 1350. Q $15E) What was the problem with the mouse in question $15D? A $15E) As Commodore was either still developing the (now more popular) 1351 mouse or the 1350 was designed as a lower cost alternative, this mouse could only emulate a joystick. When you rolled it up, the joystick "UP" pin was triggered. Likewise for the other directions. Q $15F) If you hold down the cursor key on the CBM 4000 series machine and it does not repeat, what fact about the machine do you now know? (other than the key doesn't repeat) A $15F) It is a thin 40XX machine, meaning it could not be upgraded to an 80XX machine via chip swaps. ------- A publication describing BASIC on the Commodore makes the claim that BASIC variables are limited to 5 characters, with the first two being significant. The example to prove this point in the book is given as: ABCDE=5 works, while ABCDEF=6 does not. The following questions refer to this claim: Q $160) What is wrong with the above statement? Q $161) What causes the variable ABCDEF to fail? Q $162) How long can variable names really be? Extra Credit: Who was the book publisher? ------- The Commodore LCD Computer system, much like the Commodore 65, was a product that never reached the market. Do you remember this pint-size CBM machine? Q $163) How many keys were on the CLCD keyboard? Q $164) What does LCD in the Commodore LCD stand for? Q $165) Was an internal modem to be includes? Q $166) Like the Plus/4 the CLCD unit had integrated software. What programs were included? Q $167) How many batteries of what type did the CLCD use for power? Q $168) Approximately how much did the CLCD unit weigh? Q $169) What version of BASIC was to be included with the CLCD computer? Q $16A) The CLCD unit contained a port that could be used with a Hewlett-Packard device. What did the device do? Q $16B) What microprocessor did the CLCD unit utilize? Q $16C) In addition to the usual inclusion of standard Commodore ports, what two industry standard ports were included on the CLCD? Q $16D) How much RAM did the CLCD computer include? Q $16E) How many pixels are on the LCD screen on the CLCD machine? Q $16F) How much ROM did the CLCD computer contain? ============================================================================ #(@)gfx: Hacking Graphics: Let's Get Graphical by Rick Mosdell (rick.mosdell@canrem.com) (c) September 1995 (used by permission) #(A): Introduction High resolution graphics on the C64 is not all that complicated. How to set up the VIC-chip to see your bitmap and colors IS, so this will not become a discussion on which bits to flip or where to put your blocks of data inside a computer already renowned for its lack of space. Instead, I am interested in the internal data formats of the only two graphics formats. #(A): Definitions nybble the first (lowest) or last(highest) group of 4 bits found in a: byte the fundamental 8 bit unit of our C64: an 8-bit computer. word two bytes side by side and related, ie. 16 bits. c64 pointers into RAM are lo/hi byte style. bitmap a contiguous block of data where a shape is defined when some bits or bit-pairs take a fore-ground color and others take on a background color. color-ram a block of data which defines the colors for the bitmap, thus completing the picture. color nybble since the C64 has a maximum of nybble 16 colors, to conserve space 2 colors fit into 1 byte. LORES important! This would refer to pictures created by using the normal 256-byte character set. Extensive use of the graphics characters in lowercase plus RVSon/RVSoff here. These are NOT graphic files and are text files stored in SEQ format. MEDRES this refers to Koala Paint files and related formats. HIRES this refers to Doodle files and their derivatives. #(A): Doodle! This graphics format is the simplest of all! Here the screen is divided into 64000 pixels of light (320x200) and is truly HIRES. Neither the text color-ram at $D800 nor the 4 background colors at $D021 apply at all. The downside is that only 2 colors can be displayed at one time in an 8x8 pixel block. This is the format geoPaint uses (but allows for a whole page displayed a screenful at a time). These files are prefixed with "dd..." that when compressed become "jj...". They are PRG files that load at $5C00. Internally, the 1K of color (1024 bytes) is first followed by 8K of bitmap (8192 bytes). The "dd..." files are invariably 37 blocks long, which makes sense since they are 9K long (36+ disk sectors each 254 bytes). The "1" bit of the bitmap uses the low nybble value of the color-ram while the "0" bit displays the high nybble color. Simple, straight-forward and direct! Some artists might find this color restriction too hazardous to their health. Since our screen is 320x200 pixels (64000 total remember?) dividing by 8 will give us only 8000 bytes needed for the bitmap and only 1000 bytes necessary for the color-ram. So this format actually wastes 216 bytes! Careful placement of colors can result in spectacular HIRES pictures though: look for the "Lobster" pic in geoPaint and the Doodle files "Middle Earth" and "Pagoda". Other related formats are (refer to the program "Autograph+" by Fuzzy Fox): * OCP Art Studio * RUN Paint HIRES * SID/PIC HIRES * (geoPaint) #(A): Koala Paint Koala is certainly the most colorful and interesting format. Here the screen is MEDRES, resulting in dots double pixel width for a resolution of 32000 elements (160x200). The loss of resol- ution is compensated by the ability to display 4 colors at once in each 8x8 pixel block. Here you have 2 blocks of color-ram, one wherever you put it AND the normal color-ram at $D800. Your picture is also effected screen-wide by the background color at $D021. These files, prefixed by "[cbm-1]pic...", are PRG files that load at $6000. The same files, when compressed, are prefixed by "gg...". That first character in the filename of the raw format means that you cannot delete these files normally. It is made by pressing CBM-1 (orange) and is hex $81 (decimal 129). The only way I know to scratch these files is by typing: OPEN15,DV,15,"s0:[cbm-1]pic...":close15 Where DV is your current device. Here the creators of Koala were smart and wasted NO space. Internally the file contains 8000 bytes for the bitmap(not 8192 bytes), 1000 bytes (not 1024) for the movable color-ram, 1000 bytes for the color-ram at $D800, and (this IS important) ONE more byte for the background color. The length ends up at 41 blocks which is ok considering it is about 10K long. These double width dots are called bit-pairs and they draw their colors from various sources: %00 from the background color at $D021 %01 from the high nybble of the movable color-ram %10 from the low nybble of the movable color-ram %11 from the low nybble of the normal color-ram at $D800. Interesting eh? There's your 4 colors and where they come from! Related formats would be: * Advanced OCP Art Studio * Artist64 * Blazing Paddles * RUN Paint MEDRES * SID/PIC multi-color #(A): Conclusion Although I am not an expert on graphics screens (not am I claiming to be), I think programmers should be aware of these two poular formats for storing and displaying graphics on the Commodore 64/128. ============================================================================ #(@)error: ? DS, DS$: rem The Error Channel In Commodore Hacking Issue #10, the end of the "Second SID Chip Installation" article was omitted. The omitted section is included below. (SubRef: sid) Certain early revisions of C=Hacking #10 are missing the end of the article by Alan Jones entitled "Solving Large Systems of Linear Equations on a C64 Without Memory". The omitted text is included below. (SubRef: linear) #(A)sid: Second SID Chip Installation (Line 137 on) (c) 1987 Mark A. Dickenson Here comes the difficult part to explain. This is the coupling circuit for the audio output. Here is a rough schematic. Pin 27 on 12volts dc SID chip resistor ! --. 10k ohm !collector 27!----.--/!/!/--.-----O 2n2222 --' ! ! !emitter ! ! ! <resistor ! ! >1k ! ! + <ohm ! :--!]---to RCA ! ! ! 10 uf ! ! !electrol cap ! ! ! ground--- ! ! - ! <resistor ! >1k ! <ohm ! ! ! ! ! ! ! ---ground ! - ! === 1000 pf (.001mf) ! capacitor ! ---ground - You can get the 12 volts you need for the transistor directly from pin #28 of the SID chip. If you need any help on constructing this circuit check out any of the many books that have schematics on the C-64. This is similar to the one already inside the C-64. The ground wire from the RCA plug can be soldered to the main grounding strip between the serial and video ports. The center wire will be connected to the negative side of the 10uf electrolitic capacitor. I still think you should have someone familier with electronics install this circuit for you. If you have a problem with some cartridges, you will have to install a switch between pin #25 of BOTH SID chips. This will CUT the power to the extra SID chip, effectivly turning it off. I would suggest that you turn OFF the computer before you turn the extra SID chip ON or OFF with this switch. A good place to mount the switch and RCA plug is on the back of the computer and above the monitor jack on the 64. I still haven't found a GOOD place on the 128. A suggestion was made that if you are not going to use the RF output on the computer, you can cut the wire going to that RCA plug. Then connect your audio output wire to the center connector of the plug. This does work but BE CAREFUL! Good luck on the construction. Mark A. Dickenson #(A)linear: SOLVING LARGE SYSTEMS OF LINEAR EQUATIONS ON A C64 by Alan Jones (alan.jones@qcs.org) WITHOUT MEMORY (Line 239 on) PROC slv(n#,nr#,i#,REF a(),REF c(),REF b(,),sdb#,REF sw#(),REF fail#) CL OSED // This routine solves a system of equations using the quartersolve // algorithm with partial pivoting. // It is called a "line at a time" and uses only // 0.25*nn memory locations which enables larger problems to be solved Slv calls the swap'real and swap'integer proocedures from the strings package. The strings package is a ROMMED package on the Super Chip ROM. It does exactly what it says, e.g. swap'real(a,b) is the same as: t:=a; a:=b; b:=t. Slv calls the sdot, isamax#, sswap, sscal, saxpy, and scopy routines from the blas package. The blas package is LINKed to the program, but it could, and should, be placed on EPROM. Basic Linear Algebra Subroutines, BLAS The BLAS were originally written for the Fortran language to speed execution and streamline code used for solving linear algebra and other matrix problems. The LINPACK routines, Ref. 3, use the BLAS and are perhaps the best known. The idea is that the BLAS routines will be highly optimized for a particular computer, coded in ML or a High Order Language. Some operating systems even include BLAS like routines. Writing fast efficient programs is then a simple matter of selecting the best solution algorithm and coding it in a manner that makes best use of the blas routines. There are blas routines for single precision, double precision, and complex numbers. The level 1 BLAS perform operations on rows or columns of an array and typicaly do n scalar operations replacing the inner most loop of code. There are also level 2 BLAS that perform n*n operations and Level 3 BLAS that perform n*n*n operations. Nicholas Higham has coded most of the single precision level 1 blas routines and put them in a Comal 2.0 package. The Comal blas package is included on the Packages Library Volume 2 disk. I am not aware of ML blas routines coded for any other C64/128 languages although this is certainly possible and recommended. The Comal blas routines behave exactly the same way that the Fortran blas routines do except that Fortran can pass the starting address of an array with just "a", while Comal requires "a(1)". The Comal blas will allow you pass an array, by reference, of single or multiple dimensions and start from any position in the array. If you code the blas routines as ordinary Comal routines you have to pass additional parameters and have separate routines for single dimensioned arrays and two dimensional arrays. Note also that Fortran stores two dimensional arrays by columns, and Comal (like many other languages) stores two dimensional arrays by rows. If you translate code between Fortran and Comal using blas routines you will have to change the increment variables. Fortran Comal dimension c(n), a(ilda,isda) DIM c(n#), a(lda#,sda#) scopy(n,c,1,a(i,1),ilda) scopy(n#,c(1),1,a(i#,1),1) scopy(n,c,1,a(1,j),1) scopy(n#,c(1),1,a(1,j#),sda#) The first scopy copies array c into the ith row of array a. The second scopy copies array c into the jth column of array a. This is what scopy does in Fortran: subroutine scopy(n,sx,incx,sy,incy) real sx(1),sy(1) ix=1 iy=1 do 10 i = 1,n sy(iy) = sx(ix) ix = ix + incx iy = iy + incy 10 continue return end The Comal BLAS does exactly the same thing. If coded entirely in COMAL rather than as a package it would have to be different. The call would change. scopy(n#,c(1),1,a(1,j#),sda#) would have to become, scopy(n#,c(),1,1,a(,),1,j#,sda#,sda#) and the Comal procedure might be: PROC scopy(n#, REF x(), ix#, incx#, REF y(,), iy#, jy#, sdy#, incy#) CLOSED iyinc#:=incy# DIV sdy# //assuming y is dimensioned y(?,sdy#) jyinc#:=incy# MOD sdy# FOR i#=1 TO n# DO y(iy#,jy#):=x(ix#) ix#:+incx#; iy#:+iyinc#; jy#:+jyinc# ENDFOR ENDPROC scopy Note that more information has to be passed to the procedure and used that the ML blas picks up automatically. Also we would need separate procedures to handle every combination of single and multi dimensional arrays. The Comal ML blas are indeed wonderful. For speed considerations this should also be left as an open procedure or better yet just use in line code. Here is a very simplified description of what each of the routines in the Comal BLAS package does. sum:=sasum(n#,x(1),1) Returns sum of absolute values in x(). sum:=0 FOR i#:=1 TO n# DO sum:+ABS(x(i#)) saxpy(n#,sa,x(1),1,y(1),1) Add a multiple of x() to y(). FOR i#:=1 TO n# DO y(i#):+sa*x(i#) prod:=sdot(n#,x(1),1,y(1),1) Returns dot product of x() and y(). prod:=0 FOR i#:=1 TO n# DO prod:+x(i#)*y(i#) sswap(n#,x(1),1,y(1),1) Swaps x() and y(). FOR i#:=1 TO n# DO t:=x(i#); x(i#):=y(i#); y(i#):=t scopy(n#,x(1),1,y(1),1) Copy x() to y(). For i#:=1 TO n# DO y(i#):=x(i#) max#:=isamax#(n,x(1),1) Returns index of the element of x() with the largest absolute value. t:=0; max#:=1 FOR i#:=1 TO n# IF ABS(x(i#))>t THEN t:=ABS(x(i#)); max#:=i# ENDFOR i# sscal(n#,sa,x(1),1) Scale x() by a constant sa. FOR i#:=1 TO n# DO x(i#):=sa*x(i#) snrm2(n#,x(1),1) Returns the 2 norm of x(). norm2:=0 FOR i#:=1 TO n# DO norm2:+x(i#)*x(i#) norm2:=SQR(norm2) srot(n#,x(1),1,y(1),1,c,s) Apply Givens rotation. FOR i#:=1 TO n# DO t:=c*x(i#) + s*y(i#) y(i#):=s*x(i#) + c*y(i#) x(i#):=t ENDFOR i# Bear in mind that each of these simple examples can be more complex as was given for scopy. You now have enough information to write your own BLAS routines in ML or the programming language of your choice, or to expand the BLAS routine calls in slv to ordinary in line code. You can also apply the BLAS routines in creative ways besides just operating on rows or columns. For example you could create the identity matrix with: DIM a(n#,n#) a(1,1):=1; a(1,2):=0 scopy(n#*n#-2,a(1,2),0,a(1,3),1) // zero the rest of the matrix scopy(n#-1,a(1,1),0,a(2,2),n#+1) // copy ones to the diagonal. References 1. Zambardino, R. A., "Solutions of Systems of Linear Equations with Partial Pivoting and Reduced Storage Requirements", The Computer Journal Vol. 17, No. 4, 1974, pp. 377-378. 2. Orden A., "Matrix Inversion and Related Topics by Direct Methods", in Mathematical Methods for Digital Computers, Vol. 1, Edited by A. Ralston and H. Wilf, John Wiley and Sons Inc., 1960. 3. Dongarra, J. J., Moeler, C. B., Bunch, J. R., Stewart, G. W., Linpack Users' Guide, SIAM Press, Philadelphia, 1979. ============================================================================ #(@)next: The Next Hack "But wait, there's more!" Actually, there is, but you'll have to wait until next issue for it. Here's a little appetizer of what's ahead in Commodore Hacking issue #12 o All you cross-development folks, listen up! Issue #12 will enter the realm of cross-compilers with Craig Bruce as he details the construction of a high level language cross compiler. It'll be written in C and will compile a a simplified but structured custom language including concepts from BASIC, C, and Pascal. Concepts like declaration handling, expression interpretion and optimization, and structure control definitions will be discussed o Gearing up for the 65C816S. C=Hacking will detail the new opcodes available to programmers, show how to detect CPU clock speed on any C64, accelerated or not, discuss pitfalls in code migration, and possibly give a rundown on a prototype accelerator unit from CMD. o SLIP, Sliding away.... C=Hacking will take an in-depth look at Daniel Dallmann's SLIP-DEMO program and go over the SLIP and TCP/IP protocols in detail as they relate to the Commodore. o Here Boy, here Boy! Good Dog. The "FIDO's Nuggets" column will bring readers up to date on the discussions in the FIDO CBM echo. o The RumorMonger. The best rumors we've heard so far. Your mileage may vary... o All that and C=Hacking's regular columns. So, set aside a place on that disk drive for the next issue now, because you won't want to miss it... ============================================================================ #(@)bottom |