magazines:chacking9
no way to compare when less than two revisions
Differences
This shows you the differences between two versions of the page.
— | magazines:chacking9 [2015-04-17 04:34] (current) – created - external edit 127.0.0.1 | ||
---|---|---|---|
Line 1: | Line 1: | ||
+ | < | ||
+ | ######## | ||
+ | ################## | ||
+ | ###### | ||
+ | ##### | ||
+ | ##### #### #### ## ##### #### | ||
+ | ##### ## ## #### ## ## | ||
+ | ##### | ||
+ | ##### ## ## ######## | ||
+ | ##### #### #### #### #### ##### #### | ||
+ | ##### ## | ||
+ | ###### | ||
+ | ################## | ||
+ | ######## | ||
+ | ------------------------------------------------------------------------------ | ||
+ | </ | ||
+ | ====== Editor' | ||
+ | < | ||
+ | by Craig Taylor | ||
+ | |||
+ | And *drum beat please* here's another issue of Commodore Hacking!! We've | ||
+ | lasted longer and had more issues put out than some other magazines I won't | ||
+ | discuss (*wondering where issue 39 of that mag is*). | ||
+ | |||
+ | Not many Commodore notes this time - things have gotten a little bit more | ||
+ | montatenous(sp - it's late) on the Commodore front. | ||
+ | |||
+ | I was unable to get an article by Craig Bruce in time but I got the next | ||
+ | best thing: An interview with him!! Users of his software may find this | ||
+ | interview interesting in how he looks at programming. | ||
+ | |||
+ | Right now I'm entertaining the thought of dropping C= Hacking after I | ||
+ | graduate which will be sometime around July 1st of this year. I'm interested | ||
+ | in somebody who would "carry the reign" so to speak, and take over my job of | ||
+ | nagging, bugging people etc :-) to write articles. I've got my system fairly | ||
+ | automated in handling requests here - if that person has a VAX account then | ||
+ | I could set them up with a mailserver, if a UNIX account then there' | ||
+ | of them floating on the net that could be used. _PLEASE_ write to me and | ||
+ | lemme know if you're interested. I'm going to try to get one more issue of | ||
+ | Commodore Hacking out before July 1st. | ||
+ | |||
+ | =========================================================================== | ||
+ | |||
+ | Please note that this issue and prior ones are available via anonymous FTP | ||
+ | from ccosun.caltech.edu (amongunders) under / | ||
+ | mailserver which documentation can be obtained by sending mail to | ||
+ | " | ||
+ | lines of " | ||
+ | |||
+ | =========================================================================== | ||
+ | Legal Mumbo-Jumbo | ||
+ | |||
+ | Permission is granted to re-distribut3e this " | ||
+ | freely for non-profit use. However, please contact individual authors for | ||
+ | permission to publish or re-distribute articles seperately. A charge of no | ||
+ | greater than 5 US dollars or equivlent may be charged for library service / | ||
+ | diskettte costs for this " | ||
+ | |||
+ | =========================================================================== | ||
+ | </ | ||
+ | ====== In This Issue ====== | ||
+ | < | ||
+ | |||
+ | Commodore Trivia Corner | ||
+ | |||
+ | This edition of Commodore Trivia Corner contains the answers to the July | ||
+ | edition of trivia ($070 - $07F), the questions and answers for August ($080 - | ||
+ | $08F), September ($090 - $09F), October ($0A0 - $0AF), November ($0B0 - $0BF), | ||
+ | and the questions for the December edition ($0C0 - $0CF). | ||
+ | |||
+ | A Different Perspective, | ||
+ | |||
+ | This month George and Steve continue their series on 3D graphics on the C-64 | ||
+ | with a look at hidden faces and filled faces. | ||
+ | features into last month' | ||
+ | will be discussed, such as fast multiplication (around 24 cycles) and various | ||
+ | bug fixes -- for instance, the program now works on older C-64's which | ||
+ | initialize color RAM to the background color when the screen is cleared (sorry | ||
+ | about that ;-). A very primitive form of texture mapping is also included. As | ||
+ | usual, full source and executables are included. | ||
+ | Lynx archive, so you will obviously need Lynx to get at them -- check your | ||
+ | favorite BBS or FTP site. | ||
+ | |||
+ | 2D Graphics Toolbox: Circles | ||
+ | |||
+ | To augment three-dimensional algorithms this series will focus on | ||
+ | two-dimensional drawing algortihms. | ||
+ | time around (heh -- get it?), and a very fast algorithm for drawing | ||
+ | them on your C64 is presented, with examples in assembly and BASIC7.0. | ||
+ | How fast is fast? How does 11 cycles per pixel without the use of | ||
+ | tables grab ya? | ||
+ | |||
+ | AFLI=specs v1.0 | ||
+ | |||
+ | In AFLI we can get 120 colors in theory (counted like this | ||
+ | 16!/ | ||
+ | each other we get a vision of purple - thanks the television. This article | ||
+ | details what AFLI is, how it's used and done. | ||
+ | |||
+ | Coding Tricks | ||
+ | |||
+ | Included are a series of postings to comp.sys.cbm about neat coding tricks (in | ||
+ | machine language) that are interesting and useful. | ||
+ | |||
+ | C.S.Bruce Interview | ||
+ | |||
+ | An interview with the author of Zed, the ACE os and many other numerous | ||
+ | utilities for the Commodore 64/128. | ||
+ | |||
+ | Aligning 1541 Drives | ||
+ | |||
+ | A discussion regarding Commodore 1541 disk drive alignment procedures, with | ||
+ | suggestions. | ||
+ | =========================================================================== | ||
+ | </ | ||
+ | ====== Commodore Trivia Corner ====== | ||
+ | < | ||
+ | by Jim Brain (brain@mail.msen.com) | ||
+ | |||
+ | Well, it is a new year, and I am sending up a new collection of the | ||
+ | Commodore rivia for all to enjoy. | ||
+ | following is a collection of trivia questions that I post to various | ||
+ | networks every month. | ||
+ | article. | ||
+ | the monthly winner gets a prize (Thanks to my various prize donators). | ||
+ | The whole thing is mainly just for fun, so please enjoy. | ||
+ | |||
+ | As the new year rolls in, I am happy to report the following: | ||
+ | |||
+ | 1) As I have gained access to FIDONet, the trivia is now posted to both | ||
+ | the USENET newsgroup COMP.SYS.CBM on the Internet AND the FIDONet echo | ||
+ | CBM every month. | ||
+ | |||
+ | 2) A number of publications have started publishing the trivia, including | ||
+ | | ||
+ | |||
+ | 3) I have moved into my new house (See new address at bottom). | ||
+ | may not seem important, the extra room I now have means I can now bring | ||
+ | all of my old CBM machine to the new house and work with them. Working | ||
+ | with them gives me fodder for more trivia. | ||
+ | |||
+ | As always, I welcome any questions (with answers), and encourage people | ||
+ | to enter their responses to the trivia, now at #13. Be sure you get the | ||
+ | responses to me by January 12th at noon. | ||
+ | |||
+ | |||
+ | Jim. | ||
+ | |||
+ | |||
+ | The following article contains the answers to the July edition of trivia | ||
+ | ($070 - $07F), the questions and answers for August ($080 - $08F), September | ||
+ | ($090 - $09F), October ($0A0 - $0AF), November ($0B0 - $0BF), and the | ||
+ | questions for the December edition ($0C0 - $0CF). | ||
+ | |||
+ | |||
+ | Here are the answers to Commodore Trivia Edition #8 for July, 1994 | ||
+ | |||
+ | Q $070) On a PET series computer, what visual power-on indication will tell | ||
+ | the user whether the computer has Revision 2 or Revision 3 ROMs? | ||
+ | |||
+ | A $070) Revision Level 2 ROMS (the ones with more bugs) power up with: | ||
+ | *** COMMODORE BASIC ***, with ' | ||
+ | '#' | ||
+ | |||
+ | Q $071) The IEEE-488 interface is sometimes called the GPIB interface. | ||
+ | What does GPIB stand for? | ||
+ | |||
+ | A $071) General Purpose Interface Bus. Another name is Hewlett Packard | ||
+ | Interface Bus (HPIB), since HP developed this standard for its | ||
+ | istrumentation device networking. | ||
+ | |||
+ | Q $072) Commodore manufactured at least two hard drives with IEEE-488 | ||
+ | interfaces. | ||
+ | |||
+ | A $072) The Commodore D9060 and D9090. From the cbmmodel.txt file: | ||
+ | |||
+ | * CBM D9060 5 MB Hard Drive, DOS3.0, Off-White, IEEE-488. | ||
+ | * CBM D9090 7.5 MB Hard Drive, DOS3.0, Off-White, IEEE-488. | ||
+ | |||
+ | The following model has been said to be in existence, though no one | ||
+ | has one on hand to prove it: | ||
+ | |||
+ | * CBM D9065 7.5 MB Hard Drive | ||
+ | |||
+ | And this model may never have made it past the prototype stage: | ||
+ | |||
+ | CBM D9062 Dual D9065. | ||
+ | |||
+ | Q $073) Why didn't buyers like the original PET-64? | ||
+ | |||
+ | A $073) It looked just like a old-style C-64. It had a " | ||
+ | look that the schools didn't care for. They liked the " | ||
+ | look of the PET series, so Commodore put refurbished and new 64 | ||
+ | motherboards in PET cases and sold them as PET 64s. The repackaging | ||
+ | suited the schools. | ||
+ | |||
+ | Q $074) On a PET Revision 2 ROM, what was the largest single array size that | ||
+ | BASIC could handle? | ||
+ | |||
+ | A $074) An array can have a cumulative total of 256 elements. | ||
+ | dimension arrays, that means D(0) to D(255), but a 2D array can only | ||
+ | go from DD(0,0) to DD(1,127) etc. All types of arrays had this | ||
+ | limitation. | ||
+ | |||
+ | Q $075) On the stock 1541, data is transmitted one bit at a time. How many | ||
+ | bits are transferred at a time on the Commodore 1551 disk drive? | ||
+ | |||
+ | A $075) 3 bits were transmitted at a time. I assume that each byte had a | ||
+ | parity bit tacked on for error detection, so it would have taken | ||
+ | 3 transfers to transmit a byte of information from the drives. | ||
+ | |||
+ | Q $076) On all Commodore floppy disk drives, how fast does the disk spin? | ||
+ | |||
+ | A $076) 300 RPM. | ||
+ | |||
+ | Q $077) Upon first reading the Commodore 1541 Error channel after turning | ||
+ | on the disk drive, what error number and text is returned? | ||
+ | |||
+ | A $077) 73, CBM DOS V2.6 1541, 0, 0 | ||
+ | |||
+ | Q $078) What error number and text is returned on a 1551? | ||
+ | |||
+ | A $078) 73, CBM DOS V2.6TDISK, 0, 0 | ||
+ | |||
+ | Q $079) Commodore printers are normally assigned to device #4, but they | ||
+ | can be also used as device #? | ||
+ | |||
+ | A $079) #5. The Commodore 1525 has a switch to do this, but not all printers | ||
+ | have such a switch. | ||
+ | |||
+ | Q $07A) What microprocessor is used in the Commodore 1551 disk drive? | ||
+ | |||
+ | A $07A) the 6510T. | ||
+ | on the C64. Some say it runs at 2 MHz, but the specs drives spec | ||
+ | sheet doesn' | ||
+ | |||
+ | Q $07B) When the VIC-20 was designed, the serial port throughput was roughly | ||
+ | equivalent to the throughput of the IEEE-488 bus? Why isn't it | ||
+ | very fast in production VICs? | ||
+ | |||
+ | A $07B) Let's go back to question $04F: | ||
+ | |||
+ | <begin insert> | ||
+ | Q $04F) What was the primary reason Commodore went to a serial bus | ||
+ | with the introduction of the VIC-20? | ||
+ | |||
+ | A $04F) Jim Butterfield supplied me with this one: | ||
+ | |||
+ | As you know, the first Commodore computers used the IEEE bus | ||
+ | to connect to peripherals such as disk and printer. | ||
+ | understand that these were available only from one source: | ||
+ | Belden cables. | ||
+ | career, Belden went out of stock on such cables (military | ||
+ | contract? who knows? | ||
+ | a fix: they made computers and disk drives, but couldn' | ||
+ | hook 'em together! So Tramiel issued the order: | ||
+ | computer, get off that bus. Make it a cable anyone can | ||
+ | manufacture" | ||
+ | bus was born. It was intended to be just as fast as the | ||
+ | IEEE-488 it replaced. | ||
+ | <end insert> | ||
+ | |||
+ | And here is what Jim Butterfield followed up with: | ||
+ | |||
+ | " | ||
+ | register" | ||
+ | clock) will cheerfully collect 8 bits of data without any help from | ||
+ | the CPU. At that time, it would signal that it had a byte to be | ||
+ | collected, and the processor would do so, using an automatic | ||
+ | handshake built into the 6522 to trigger the next incoming byte. | ||
+ | Things worked in a similar way outgoing from the computer, too. | ||
+ | We early PET/CBM freaks knew, from playing music, that there was | ||
+ | something wrong with the 6522's shift register: | ||
+ | other functions. | ||
+ | the tape! (The shift register was a popular sound generator). | ||
+ | the Commodore engineers, who only made the chip, didn't know this. | ||
+ | Until they got into final checkout of the VIC-20. | ||
+ | |||
+ | By this time, the VIC-20 board was in manufacture. | ||
+ | be designed in a few months (yes, the silicon guys had application | ||
+ | notes about the problem, long since), but it was TOO LATE! | ||
+ | |||
+ | A major software rewrite had to take place that changed the VIC-20 | ||
+ | into a " | ||
+ | eight times as much work on the part of the CPU; and unlike the shift | ||
+ | register plan, there was no timing/ | ||
+ | thing slowed down by a factor of approximately 5 to 6. | ||
+ | |||
+ | When the 64 came out, the problem VIA 6522 chip had been | ||
+ | replaced by the CIA 6526. This did not have the shift register | ||
+ | problem which had caused trouble on the VIC-20, and at that time it | ||
+ | would have been possible to restore plan 1, a fast serial bus. Note | ||
+ | that this would have called for a redesign of the 1540 disk drive, | ||
+ | which also used a VIA. As best I can estimate - and an article in | ||
+ | the IEEE Spectrum magazine supports this - the matter was discussed | ||
+ | within Commodore, and it was decided that VIC-20 compatibility was | ||
+ | more important than disk speed. | ||
+ | redesign was an important part of the decision, since current | ||
+ | inventories needed to be taken into account. | ||
+ | Commodore 64 as a " | ||
+ | |||
+ | The higher-resolution screen of the 64 (as compared to the VIC-20) | ||
+ | could not be supported without stopping the CPU every once in a while. | ||
+ | To be exact: | ||
+ | had to be put into a WAIT condition for 42 microseconds, | ||
+ | allow the next line of screen text and color nybbles to be swept into | ||
+ | the chip.(More time would be needed if sprites were being used). | ||
+ | But the bits were coming in on the serial bus faster than that: | ||
+ | a bit would come in about every 20 microseconds! | ||
+ | frozen for longer than that, would miss some serial bits completely! | ||
+ | Commodore' | ||
+ | That's why the VIC-20 has a faster serial bus than the 64, even though | ||
+ | the 64 was capable, technically, | ||
+ | |||
+ | Fast disk finally came into its own with the Commodore 128." | ||
+ | |||
+ | --Jim | ||
+ | |||
+ | Q $07C) On Commodore computers, how much RAM is set aside as a tape buffer? | ||
+ | |||
+ | A $07C) 192 bytes is used as a tape buffer. | ||
+ | bytes long. | ||
+ | |||
+ | Q $07D) On Commodore computers, most every peripheral has a device number. | ||
+ | What is the device number of the screen? | ||
+ | |||
+ | A $07D) #3 | ||
+ | |||
+ | Q $07E) What is the device number of the keyboard? | ||
+ | |||
+ | A $07E) #0 | ||
+ | |||
+ | Q $07F) Commodore computers use 2' | ||
+ | What is the 2' | ||
+ | |||
+ | A $07F) (This was not a Commodore specific question) | ||
+ | use this notation to represent integer quantities. | ||
+ | notation, a -1 looks like 11111111(binary) or $FF(hex). | ||
+ | |||
+ | |||
+ | Here are the answers to Commodore Trivia Edition #9 for August, 1994 | ||
+ | |||
+ | Q $080) During the days of the Commodore 64 and the VIC-20, Commodore | ||
+ | produced at least two Commodore magazines. | ||
+ | |||
+ | A $080) The magazines were originally called " | ||
+ | " | ||
+ | down the name of the latter as I see " | ||
+ | " | ||
+ | Commodore Microcomputers started its life in 1979, whereas | ||
+ | " | ||
+ | around 1987, when they were merged to form " | ||
+ | Then, around 1990, the magazine was sold to IDG Communications and | ||
+ | was merged into RUN. RUN was continued for a while, but was finally | ||
+ | pulled out of circulation. | ||
+ | rights to the magazine, and now Commodore World is being produced by | ||
+ | CMD. I am not sure how strong (if any) a link there is between | ||
+ | RUN and CW, but some of the same authors write for the new | ||
+ | publication. | ||
+ | |||
+ | Commodore Microcomputers (Commodore Magazine) | ||
+ | Power/ | ||
+ | RUN (Commodore/ | ||
+ | |||
+ | "The Transactor" | ||
+ | |||
+ | Q $081) Back in the PET heyday, another magazine was produced by Commodore | ||
+ | Canada. | ||
+ | journal. | ||
+ | |||
+ | A $081) The infamous " | ||
+ | magazines, it was originally published by Commodore Canada, before | ||
+ | being sold to an individual named Mr. Hilden. | ||
+ | 0838-0163. | ||
+ | but ceased to exist in 1989-90. | ||
+ | 1978. | ||
+ | |||
+ | Q $082) The Commodore 128 has a VIC-II compatible chip inside it. Can this | ||
+ | chips be switched for a VIC-II from a Commodore 64? | ||
+ | |||
+ | A $082) No! The newer 128 compatible chip (VIC-IIe) has 8 extra pins to | ||
+ | perform timing functions specific for the 128. In addition, some of | ||
+ | the registers have extra functions. | ||
+ | to make it compatible can be made. | ||
+ | |||
+ | Q $083) What does the video encoding standard PAL expand to? | ||
+ | |||
+ | A $083) Phase Alternating Line is the answer I was looking for, which | ||
+ | describes the video encoding used in Europe, but Programmable Array | ||
+ | Logic is also correct, which describes the family of chips used as | ||
+ | " | ||
+ | |||
+ | Q $084) How many buttons were present on the earliest of Commodore tape decks? | ||
+ | |||
+ | A $084) 5: Play, Rewind, Fast-Forward, | ||
+ | separated the stop and eject functions into two buttons. | ||
+ | |||
+ | Q $085) Earlier SID chips had a distinctive " | ||
+ | coders used to an advantage. | ||
+ | click, and then later reintroduced it. When does the telltale click | ||
+ | occur? | ||
+ | |||
+ | A $085) When you change the volume of a voice. | ||
+ | outputting anything. | ||
+ | |||
+ | Q $086) What does CP/M stand for? | ||
+ | |||
+ | A $086) Take your pick: | ||
+ | |||
+ | Control Program/ | ||
+ | Control Program for Microprocessors | ||
+ | Control Program for Microcomputers. | ||
+ | |||
+ | The last one is considered by many to be most correct. | ||
+ | |||
+ | Q $087) What is the highest line number allowed for a program line in | ||
+ | Commodore BASIC V2? | ||
+ | |||
+ | A $087) Normally, the user cannot enter a line number higher than 63999. | ||
+ | If you want to be tricky, however, the numbers can be made to go up | ||
+ | to 65535. | ||
+ | |||
+ | Q $088) What symbol, clearly printed on the front of a key on the Commodore | ||
+ | VIC, 64, and 128 keyboard, is not available when the lower case | ||
+ | character set is switched in? | ||
+ | |||
+ | A $088) The PI symbol. | ||
+ | a checkerboard-like character when in lower-case mode. Unlike the | ||
+ | graphics characters printed on the fronts of the keys, this one is | ||
+ | positioned in the middle of the keycap, and should probably be | ||
+ | accessible in both character sets. | ||
+ | |||
+ | Q $089) How do you get the " | ||
+ | |||
+ | A $089) In lowercase mode, type a shift-@ | ||
+ | |||
+ | Q $08A) On the PET computers, what memory location holds the Kernal ROM | ||
+ | version? | ||
+ | |||
+ | A $08A) It is different from the 64/ | ||
+ | ROMs, while 1 indicates new ROMs. | ||
+ | |||
+ | Q $08B) The Commodore computers have 2 interrupts, called IRQ and NMI. | ||
+ | What does IRQ stand for? | ||
+ | |||
+ | A $08B) Interrupt ReQuest. | ||
+ | usually be allowed to interrupt the processor. | ||
+ | be masked off by the SEI instruction. | ||
+ | |||
+ | Q $08C) What does NMI stand for? | ||
+ | |||
+ | A $08C) Non-Maskable Interrupt. | ||
+ | masked by an instruction. | ||
+ | mask it. | ||
+ | |||
+ | Q $08D) The 6502 line of microprocessors has a number of flags that can be | ||
+ | used to test for certain conditions. | ||
+ | What does it stand for? | ||
+ | |||
+ | A $08D) ' | ||
+ | is set to be equal to bit 7 of the result of the instruction. | ||
+ | |||
+ | Q $08E) How about the D flag? | ||
+ | |||
+ | A $08E) It stands for decimal mode. This mode causes certain instructions | ||
+ | to treat a byte as 2 4 bit BCD-coded nybbles. | ||
+ | |||
+ | Q $08F) The shorthand for the BASIC keyword PRINT is '?' | ||
+ | shorthand equivalent for PRINT#? | ||
+ | |||
+ | A $08F) pR is the way to abbreviate PRINT# | ||
+ | |||
+ | |||
+ | Here are the answers to Commodore Trivia Edition #10 for September, 1994 | ||
+ | |||
+ | Q $090) The 6502 has a rich history. | ||
+ | microprocessor. | ||
+ | |||
+ | A $090) The 65XX series of processors was modeled after the Motorola 6800. | ||
+ | Motorola hampered the design groups' | ||
+ | developments using the 6800. A core group of 8 designers left Motorola | ||
+ | and went to MOS Technologies, | ||
+ | calculator chips at the time. MOS decided it was time to go into | ||
+ | the CPU business. | ||
+ | |||
+ | Q $091) The 6502 has a older brother that was never produced. | ||
+ | number designation and why it was not produced. | ||
+ | |||
+ | A $091) The older brother to the 6502 was the 6501. The 6501 was | ||
+ | pin-compatible with the 6800, which prompted a suit by Motorola. | ||
+ | Eventually, MOS reached an agreement where they scrapped the 6501 | ||
+ | marketing, but were free to market the 6502. | ||
+ | |||
+ | Q $092) How many different opcodes are considered valid and " | ||
+ | MOS NMOS 6502 line? | ||
+ | |||
+ | A $092) 151 opcodes are documented in the NMOS 6502 data book. The remaining | ||
+ | 105 opcodes were not implemented, | ||
+ | in the opcode matrix. | ||
+ | opcodes will actually perform pieces of two or more valid opcodes. | ||
+ | Newer CPU systems trap all non-implemented opcode usages, but not | ||
+ | the 6502. | ||
+ | |||
+ | Q $093) Every instruction takes at least __ cycles to complete. | ||
+ | the missing number. | ||
+ | |||
+ | A $093) 2. The architecture assumes that each opcode has two bytes in it and | ||
+ | one byte can be fetched per cycle. | ||
+ | 1 byte, the extra fetched byte (actually the next opcode), is thrown | ||
+ | away. | ||
+ | |||
+ | Q $094) Which instructions take more time than necessary as a result of the | ||
+ | answer to Q $093? | ||
+ | |||
+ | A $094) Although this is a subjective answer, One could nominate NOP on the | ||
+ | basis that NOP is generally believed to waste one execution cycle on | ||
+ | a particular processor, namely one cycle on the 65XX line. However, | ||
+ | one can argue that NOP simply means no operation, and has no ties to | ||
+ | length of execution. | ||
+ | |||
+ | All other instructions must take at least two cycles: one for opcode | ||
+ | fetch, one for operation. | ||
+ | |||
+ | Q $095) What did MOS Technologies manufacture befor introducing the 650X line | ||
+ | of microprocessors? | ||
+ | |||
+ | A $095) As stated above, it was calculator chips. | ||
+ | |||
+ | Q $096) Three companies manufactured the 6502 under a cross-licensing | ||
+ | agreement. | ||
+ | |||
+ | A $096) Rockwell, MOS Technologies, | ||
+ | |||
+ | Q $097) In NTSC-land, how fast does the 1MHz 6510 in the C64 actually run? | ||
+ | |||
+ | A $097) 1.022727143 MHz. It is derived by taking the main clock frequency | ||
+ | (14.31818MHz) and diving it by 14. | ||
+ | |||
+ | Q $098) What about in PAL-land? | ||
+ | |||
+ | A $098) 985.248449 kHz. It is derived by taking the main clock frequency | ||
+ | (17.734472MHz) and dividing it by 18. Thus the PAL 64 actually runs | ||
+ | slower than the NTSC one. | ||
+ | |||
+ | Q $099) Data is latched into the 650X microprocessor on the (rising/ | ||
+ | edge? | ||
+ | |||
+ | A $099) Data is latched in to the 65XX on the falling edge of Phi0 (Phi1). | ||
+ | The timing diagram in some books (64 PRG is one) is incorrect. | ||
+ | |||
+ | Q $09A) Through the years, the 650X line has changed family numbers, yet | ||
+ | the part has not been changed. | ||
+ | digits in this case) Name the other family numbers used by MOS to | ||
+ | denote the 650X line. | ||
+ | |||
+ | A $09A) the 75XX line used in the 264 series (Plus/4 and C16), and the 85XX | ||
+ | series used in the C64C and C128 series. | ||
+ | |||
+ | Q $09B) Consider the following code: | ||
+ | |||
+ | ldx #10 | ||
+ | lda $ff,x | ||
+ | |||
+ | what location does the accumulator get loaded with? | ||
+ | |||
+ | A $09B) The answer is location $ff+10 mod 256 = $09. | ||
+ | The answer involves explaining a (mis)features of the NMOS 65XX CPU | ||
+ | line. The above code instructs the 65XX CPU to use zero-page | ||
+ | addressing mode to load the accumulator. | ||
+ | address need only be one byte wide ($ff in this case), because the | ||
+ | high byte is considered to be $00. Now, as humans, we would expect | ||
+ | the CPU would add 10 to 255 ($ff), giving 265 ($109) as the address | ||
+ | to load the accumulator from. However, the CPU designers decided | ||
+ | that zero-page addressing means that the high byte will be $00 all the | ||
+ | time, no exceptions. | ||
+ | low byte of the addition will be used as the low byte of the address | ||
+ | (9 in this case), but the high-byte will be ZERO. All zero page | ||
+ | addressing modes work this way. Note that the CMOS versions of the | ||
+ | 6502 do perform the high byte " | ||
+ | on the NMOS parts. | ||
+ | |||
+ | Q $09C) What about the following? | ||
+ | |||
+ | ldx #10 | ||
+ | lda ($ff),x | ||
+ | |||
+ | A $09C) This was a trick. | ||
+ | mode using the x register, but that addressing mode can only be used | ||
+ | with the y register. | ||
+ | code: | ||
+ | |||
+ | ldx #10 | ||
+ | lda ($ff),y | ||
+ | |||
+ | Then, the above discussion for zero-page addressing holds true here | ||
+ | as well. The effective address would have been (hi:lo) $100:$0ff, but | ||
+ | is instead (hi:lo) $000: | ||
+ | exactly that. There is no way to address outside of zero-page with | ||
+ | zero-page addressing. | ||
+ | |||
+ | Q $09D) How many CPU clock signal lines does the 650X require to run? | ||
+ | |||
+ | A $09D) 1. The 6501 used two, as the 6800 used two, but the 6502 and | ||
+ | successors only required Phi0 (Phi1). | ||
+ | |||
+ | Q $09E) Where does the 650X line fetch its first byte from after reset? | ||
+ | |||
+ | A $09E) $fffc. | ||
+ | the IP, and the code is read starting there. | ||
+ | since the 65XX line stores addresses in low byte, high byte format. | ||
+ | |||
+ | Q $09F) One of the original designers on the NMOS 6502 CPU now heads up | ||
+ | Western Design Center in Arizona, and makes the 65C02 and 65C816 | ||
+ | CPU chips. | ||
+ | |||
+ | A $09F) Bill Mensch. | ||
+ | in the same manner he and Chuck Peddle and others hand-designed the | ||
+ | 6501 and 6502. | ||
+ | |||
+ | |||
+ | Here are the answers to Commodore Trivia Edition #11 for October, 1994 | ||
+ | |||
+ | Q $0A0) In the mid 1980' | ||
+ | Commodore 64, 64C, 128, and 128D. There were three of them. Give | ||
+ | their model numbers, and what was different among them. | ||
+ | |||
+ | A $0A0) The 1700 (128kB), the 1764 (256kB), and the 1750 (512kB). | ||
+ | 1700 and the 1750 were marketed for the 128, while the 1764 was | ||
+ | marketed from the 64 line. | ||
+ | |||
+ | Q $0A1) Some of the CIA integrated circuits used on the C64 and C128 | ||
+ | computers have a hardware defect. | ||
+ | defect, and when does it occur? (May be more than one, but I need | ||
+ | only one) | ||
+ | |||
+ | A $0A1) The only one I have documented in front of me is the timer B | ||
+ | interrupt bug, which is explained in the " | ||
+ | by George Hug in Transactor 9.3. (1) However, I had many people | ||
+ | relate other bugs (2 and 3), which I haven' | ||
+ | add them as possibilities. (I encourage readers to confirm/ | ||
+ | latter 2.) | ||
+ | |||
+ | 1) If timer B of the 6526 CIA times out at about the same time as a | ||
+ | read of the interrupt register, the timer B flag may not be set at | ||
+ | all, and no interrupt will occur if timer B interrupts were | ||
+ | | ||
+ | |||
+ | 2) When the hour on the TOD clock is 12, the AM/PM must be reversed | ||
+ | from its normal setting to set/reset the AM/PM flag. | ||
+ | |||
+ | 3) The TOD clock sometimes generates double interrupts for alarm | ||
+ | | ||
+ | |||
+ | |||
+ | Q $0A2) Name the Commodore machine(s) on which a Intel 8088 was an OPTIONAL | ||
+ | coprocessor. | ||
+ | |||
+ | A $0A2) I was looking for the B series computers, which contains the B | ||
+ | computers (B128, B256), as well as the 600 series and the 700 | ||
+ | series. | ||
+ | processor on a separate card. However, another correct answer is | ||
+ | the Amiga, which can have a 8088 attached via an expansion card or a | ||
+ | SideCar(tm) unit. | ||
+ | |||
+ | Q $0A3) On Commodore computers beside the Plus/4 series, there are three | ||
+ | frequencies used to record the data on the tape. Name the | ||
+ | frequencies used. | ||
+ | |||
+ | A $0A3) 1953.125Hz, 2840.909Hz, and 1488.095Hz. | ||
+ | waveforms with periods: 512us, 352us, and 672us, respectively. | ||
+ | |||
+ | Q $0A4) Commodore Plus/4 series computers can not read any cassettes | ||
+ | recorded on other Commodore computers. | ||
+ | nothing to do with the nonstandard connecotr on the Plus/4) | ||
+ | |||
+ | A $0A4) The tones recorded on the Plus/4-C16 are exactly one-half the | ||
+ | frequencies shown above. | ||
+ | and C16 were supposed to run at twice its present frequency, | ||
+ | but were downgraded at the last-minute, | ||
+ | the tones was not updated to reflect the change. | ||
+ | heresay, so you decide for yourself. | ||
+ | |||
+ | Q $0A5) During power-up, the Commodore 64 checks to see if it running | ||
+ | in PAL-land or NTSC-land. | ||
+ | |||
+ | A $0A5) It sets the raster compare interrupt to go off at scan line 311. | ||
+ | If the interrupt occurs, we are on a PAL system, since NTSC will | ||
+ | never get to line 311 (NTSC only has 262.5 lines per frame, every | ||
+ | other frame shifted down a bit to create 525 lines). | ||
+ | |||
+ | Q $0A6) What is the 65XX ML opcode for BRK? | ||
+ | |||
+ | A $0A6) $00, or 00 | ||
+ | |||
+ | Q $0A7) On the 65XX CPU, what gets pushed onto the stack when an interrupt | ||
+ | occurs? | ||
+ | |||
+ | A $0A7) The program counter gets saved high byte first, then the processor | ||
+ | status flags get saved. | ||
+ | |||
+ | Q $0A8) Speaking of the stack, where is the stack located in the 65XX address | ||
+ | map? | ||
+ | |||
+ | A $0A8) $0100 to $01FF | ||
+ | |||
+ | Q $0A9) On the 65XX CPU line, it is possible to set and clear a number of | ||
+ | processor status flags. | ||
+ | clear the carry flag. What flag has a clear opcode, but no set | ||
+ | opcode? | ||
+ | |||
+ | A $0A9) The overflow flag: V. However, the V flag can be set via an external | ||
+ | pin on some members of the 65XX line. The 1541 uses this as an | ||
+ | ingenious synchronization tool. | ||
+ | |||
+ | Q $0AA) When saving a text file to tape, the computer records 192 bytes of | ||
+ | data, an inter-record gap, and then the same 192 bytes of data | ||
+ | again. | ||
+ | |||
+ | A $0AA) Some terminology: | ||
+ | that a tape block is recorded twice on the tape, but Commodore | ||
+ | considers the two copies and the gap between them a single | ||
+ | " | ||
+ | two dissimilar records. | ||
+ | the interrecord gap is nominally 2 seconds long, (or 223.2 byte | ||
+ | lengths, although the gap contains no data). | ||
+ | the tape motors to get up to speed before the next data comes under | ||
+ | the read/write head. The tape motors may need to stop between | ||
+ | records if the program is not requesting any more data from the | ||
+ | tape data file at this time. If the program subsequently asks | ||
+ | for data from the tape, the drive must get up to speed before the | ||
+ | read can occur. | ||
+ | gap was too small, so programmers had problems retrieving data | ||
+ | files. | ||
+ | |||
+ | For completeness, | ||
+ | copies of the data) consists of 50+ short pulses, each of which is | ||
+ | 352us in length, giving a timing of .0176s+. | ||
+ | copy important data to safe locations, reset pointers, and do error | ||
+ | logging. | ||
+ | |||
+ | Q $0AB) On an unexpanded VIC-20, where does the screen memory start? | ||
+ | |||
+ | A $0AB) $1e00, or 7680 | ||
+ | |||
+ | Q $0AC) In Commodore BASIC, what is the abbreviated form of the " | ||
+ | command? | ||
+ | |||
+ | A $0AC) lO (L SHIFT-O) | ||
+ | |||
+ | Q $0AD) In Commodore BASIC, what is the abbreviated form of the " | ||
+ | command? | ||
+ | |||
+ | A $0AD) lI (L SHIFT-I) | ||
+ | |||
+ | Q $0AE) On the Commodore 64, there is section of 4 kilobytes of RAM that | ||
+ | cannot be used for BASIC programs. | ||
+ | places for many ML programs, however. | ||
+ | memory? | ||
+ | |||
+ | A $0AE) $c000, or 49152 | ||
+ | |||
+ | Q $0AF) What is stored at locations $A004-$A00B, | ||
+ | |||
+ | A $0AF) The text " | ||
+ | text is not referenced by any routine. | ||
+ | strange because the code is Microsoft' | ||
+ | |||
+ | |||
+ | Here are the answers to Commodore Trivia Edition #12 for November, 1994 | ||
+ | |||
+ | Q $0B0) What will happen if you type ?"" | ||
+ | on the PET series, the 64 series, or the 128 series? | ||
+ | |||
+ | A $0B0) The BASIC interpreter has a bug in it that shows up while interpreting | ||
+ | the above statement. | ||
+ | stack prior to returning from a subroutines call. At least on the | ||
+ | C64, the two bytes are both zeros. | ||
+ | address on the stack, the return retrieves the two bytes left on the | ||
+ | stack and attempts to se that as the return address. | ||
+ | what code it executes after the return, it can do a number of things. | ||
+ | |||
+ | Most of the time after the bug occurs, the interpreter limps along | ||
+ | for a while until it hits a BRK instruction, | ||
+ | instruction causes the system to execute an interrupt. | ||
+ | the system vectors through $316-$317 (BRK vector) and does a warm | ||
+ | start. | ||
+ | internal machine language monitor. | ||
+ | do something with the BRK vector, the machine will hang. | ||
+ | |||
+ | Now, note that the above is not the only result. | ||
+ | interpreter is executing code from the wrong location, any result | ||
+ | from no effect to hung machine is possible. | ||
+ | |||
+ | Note that this is NOT normal behavior. | ||
+ | error while interpreting the above statement. | ||
+ | |||
+ | Q $0B1) In the first CBM 64 units, what color was the screen color RAM | ||
+ | changed to when you cleared the screen? | ||
+ | |||
+ | A $0B1) The screen color RAM was changed to value 1 when the screen was | ||
+ | cleared. | ||
+ | character was white on the screen. | ||
+ | with the normal blue background. | ||
+ | |||
+ | Q $0B2) Why was it changed in later versions of the 64? | ||
+ | |||
+ | A $0B2) Commodore found that this practice sometimes caused "light flashes" | ||
+ | during screen scrolls. | ||
+ | but ... The change was to make the color RAM equal to background | ||
+ | color register #0. Well, this got rid of the "light flashes", | ||
+ | then poking values to screen RAM caused invisible characters, since | ||
+ | the foreground color of the character was the same as the background | ||
+ | color of the screen. | ||
+ | |||
+ | Well, this broke a number of older programs that did not | ||
+ | properly initialize the color RAM. Also, Commodore fixed the problem | ||
+ | with the VIC-II that had caused these "light flashes" | ||
+ | changed the KERNAL a third time. Since the above change caused | ||
+ | invisible characters, Commodore made a third revision that changed | ||
+ | the color RAM to the value in location 646 (the current cursor | ||
+ | foreground color). | ||
+ | |||
+ | Q $0B3) What is " | ||
+ | error" in CBM BASIC? | ||
+ | |||
+ | A $0B3) The text is actually "? | ||
+ | spaces between " | ||
+ | expanantion: | ||
+ | |||
+ | "The vector at $0300 points to a routine at $A43A, which is the | ||
+ | general error message printing routine. | ||
+ | the error, and it prints it. it looks up the address of the error | ||
+ | text from a table, then prints the text, which does not have any | ||
+ | trailing spaces. | ||
+ | does this for all errors." | ||
+ | |||
+ | Historically, | ||
+ | columns. | ||
+ | code, someone noticed that the some of the error strings would | ||
+ | span two VIC-20 lines. | ||
+ | a little, so that they all printed neatly on two lines: | ||
+ | error string: | ||
+ | ? | ||
+ | ? | ||
+ | ERROR (carriage return plus one space). | ||
+ | When the C64 BASIC was being ported from the VIC-20, the carriage | ||
+ | return was replaced with a space character. | ||
+ | |||
+ | I admit this caught me by surprise. | ||
+ | for years, and never noticed that "? | ||
+ | |||
+ | Q $0B4) On what Commodore machine was the operating system OS/9 available? | ||
+ | |||
+ | A $0B4) Since OS/9 was a real-time operating system for the 6809 | ||
+ | microprocessor, | ||
+ | had two different names: | ||
+ | sold as the "MMF (Micro MainFrame) 9000 in Germany, and its model | ||
+ | number was SP9000. | ||
+ | |||
+ | Q $0B5) Which Commodore machine(s) does not have a user port? | ||
+ | |||
+ | A $0B5) There were a number of answers to this question, and there may be | ||
+ | more: | ||
+ | |||
+ | The Commodore C16. Commodore decided to cut out telecommunications, | ||
+ | and thus designed the user port out of the computer, as the modem is | ||
+ | the only use Commodore ever made of the user port. This also | ||
+ | includes the C116, a version of the C16 with a chicklet keyboard. | ||
+ | |||
+ | The Commodore Ultimax/MAX machine. | ||
+ | console produced in the early 80s. It was basically a stripped down | ||
+ | Commodore 64. | ||
+ | |||
+ | The 64 GS (Game System). | ||
+ | in the late 80s. | ||
+ | |||
+ | Q $0B6) How many pins are there in a Commodore Serial Connector? | ||
+ | |||
+ | A $0B6) 6. | ||
+ | |||
+ | Q $0B7) There are 13 addressing modes available on the 6502. Name them. | ||
+ | |||
+ | A $0B7) No# Name | ||
+ | --- ------------ | ||
+ | 01) accumulator | ||
+ | 02) immediate | ||
+ | 03) zero page lda $00 | ||
+ | 04) zero page, | ||
+ | 05) zero page, | ||
+ | 06) absolute | ||
+ | 07) absolute, | ||
+ | 08) absolute, | ||
+ | 09) implied | ||
+ | 10) relative | ||
+ | 11) (indirect, | ||
+ | 12) (indirect), | ||
+ | 13) (absolute indirect) | ||
+ | |||
+ | Q $0B8) If you were to put one large sequential file onto an 8050 disk drive, | ||
+ | how big could that file be? | ||
+ | |||
+ | A $0B8) According to the 8050 User Manual, a sequential file could be | ||
+ | 521208 bytes in size. | ||
+ | |||
+ | Q $0B9) How many characters can be present in a standard Commodore DOS | ||
+ | filename? | ||
+ | |||
+ | A $0B9) 16 characters. | ||
+ | |||
+ | Q $0BA) How many pins does a 6502 IC have on it? | ||
+ | |||
+ | A $0BA) 40 pins. | ||
+ | |||
+ | Q $0BB) How many pins does the standard IEEE-488 connector have on it? | ||
+ | |||
+ | A $0BB) 24 pins. | ||
+ | |||
+ | Q $0BC) On the IEEE-488 bus, what does the acronym for pin 7, NRFD, stand for? | ||
+ | |||
+ | A $0BC) Not Ready For Data. | ||
+ | |||
+ | Q $0BD) On the NMOS 6502, what is the ML opcode for SED, and what does this | ||
+ | opcode do? | ||
+ | |||
+ | A $0BD) $f8, SEt Decimal mode. Sets the D flag in the status flags byte. | ||
+ | Although used rarely, this opcode switches on Binary Coded Decimal | ||
+ | mode. In BCD mode, the byte $10 is treated as 10, not 16. The add | ||
+ | and subtract instructions are the only legal ones affected by this | ||
+ | mode, although some undocumented/ | ||
+ | For example, in this mode, adding the byte $15 (21) to the byte $25 | ||
+ | (37) yields $40 (64) not $3a (58). emember that, in this mode, | ||
+ | $40 = 40, not 64. | ||
+ | |||
+ | Q $0BE) Assuming a PET computer and a non-PET computer have access to a | ||
+ | common disk drive or tape drive, there are two ways to load a PET | ||
+ | BASIC program on the non PET CBM computer. Name them. | ||
+ | |||
+ | A $0BE) Most differing series of Commodore computers had different places | ||
+ | for the start of BASIC programs. | ||
+ | (2049) is the start of BASIC memory, but most PET computers start | ||
+ | BASIC memory at $0401 (1025). | ||
+ | BASIC programs are stored on tape and disk with the start address, | ||
+ | and the line links in a BASIC program have absolute addresses in them. | ||
+ | To fix these problems, the Commodore VIC-20 and newer computers came | ||
+ | out with a " | ||
+ | |||
+ | 1) Save the program on the PET like so: save " | ||
+ | Then, you could load the program into the non-PET machine | ||
+ | by using a relocatable load: load " | ||
+ | program in at start of BASIC memory and refigure the line links. | ||
+ | |||
+ | 2) Redefine start of BASIC memory on non-PET machine. | ||
+ | of pokes to relevant BASIC pointers, and the start of BASIC | ||
+ | was moved. Then, load the program non-relocatable. | ||
+ | |||
+ | Now, from the above discussion, it looks like option 1 is the | ||
+ | simplest route. | ||
+ | Earlier PET computers saved the BASIC program from $0400, not | ||
+ | $0401 as is expected. | ||
+ | would have a zero byte as the first byte of the program. | ||
+ | fix: change BASIC pointer to itself-1, load PET program, reset | ||
+ | BASIC pointer. | ||
+ | |||
+ | Q $0BF) Only one of the ways detailed in $0BE works the other way around. | ||
+ | Which one? | ||
+ | |||
+ | A $0BF) Since the earlier PET computers did not have a " | ||
+ | the only way to load a program from, say, a C64 into an 2001 was to | ||
+ | use option #2 above and move the start of BASIC memory to $0801 | ||
+ | (2049). | ||
+ | |||
+ | |||
+ | Commodore Trivia Edition #13 | ||
+ | |||
+ | Q $0C0) The early 1541 drives used a mechanism developed by ______. | ||
+ | the company. | ||
+ | |||
+ | Q $0C1) On later models, Commodore subsequently changed manufacturers | ||
+ | for the 1541 drive mechanism. | ||
+ | |||
+ | Q $0C2) What is the most obvious difference(s). | ||
+ | necessary) | ||
+ | |||
+ | Q $0C3) On Commodore BASIC V2.0, what answer does the following give: | ||
+ | PRINT (SQR(9)=3) | ||
+ | |||
+ | Q $0C4) In Commodore BASIC (Any version) what does B equal after the following | ||
+ | runs: C=0:B=C=0 | ||
+ | |||
+ | Q $0C5) The first PET cassette decks were actually _______ brand cassette | ||
+ | players, modified for the PET computers. | ||
+ | |||
+ | Q $0C6) In Commodore BASIC (Any version), what happens if the following | ||
+ | program is run: | ||
+ | |||
+ | 10 J=0 | ||
+ | 20 IF J=0 GO TO 40 | ||
+ | 30 PRINT " | ||
+ | 40 PRINT " | ||
+ | |||
+ | Q $0C7) In question $068, we learned how Jack Tramiel first happened upon the | ||
+ | name " | ||
+ | was he in when he first saw it? | ||
+ | |||
+ | Q $0C8) On the Commodore user port connector, how many edge contacts are | ||
+ | there? | ||
+ | |||
+ | Q $0C9) On most Commodore computers, a logical BASIC line can contain up to | ||
+ | 80 characters. | ||
+ | |||
+ | Q $0CA) If a file is saved to a Commodore Disk Drive with the following | ||
+ | characters: chr$(65); | ||
+ | entry look like? | ||
+ | |||
+ | Q $0CB) What is the maximum length (in characters) of a CBM datasette | ||
+ | filename? | ||
+ | |||
+ | Q $0CC) How many keys are on a stock Commodore 64 keyboard? | ||
+ | |||
+ | Q $0CD) Commodore BASIC uses keyword " | ||
+ | 129 becomes " | ||
+ | parenthesis as well as a BASIC keyword? | ||
+ | |||
+ | Q $0CE) There are 6 wires in the Commodore serial bus. Name the 6 wires. | ||
+ | |||
+ | Q $0CF) On the Commodore datasette connector, how many logical connections are | ||
+ | there? | ||
+ | |||
+ | Some are easy, some are hard, try your hand at: | ||
+ | |||
+ | Commodore Trivia Edition #13! | ||
+ | |||
+ | Jim Brain | ||
+ | brain@mail.msen.com | ||
+ | 602 North Lemen (New address) | ||
+ | Fenton, MI 48430 | ||
+ | (810) 737-7300 x8528 | ||
+ | |||
+ | ============================================================================== | ||
+ | </ | ||
+ | ====== A Different Perspective, | ||
+ | < | ||
+ | by George Taylor (aa601@cfn.cs.dal.ca) and Stephen Judd (sjudd@nwu.edu). | ||
+ | |||
+ | We... are... VR Troopers! | ||
+ | excursion out of the three dimensional world and into our own little virtual | ||
+ | world inside the C64. So sit back in your virtual chair, put on your virtual | ||
+ | thinking helmet, maybe grab a virtual beer, and prepare for a virtually | ||
+ | useful experience with another virtually humongous article. | ||
+ | |||
+ | Last time we laid down the foundations of 3D graphics: rotations and | ||
+ | projections. | ||
+ | at hidden surfaces as well as filled surfaces. | ||
+ | the old program so that it is a little more efficient by for instance | ||
+ | introducing a much faster multiplication routine and moving all variables | ||
+ | into zero-page. | ||
+ | |||
+ | To get us in the mood let's review from last time. We are in a | ||
+ | three-dimensional space; in particular, a right-handed three-dimensional | ||
+ | space, so that the x-axis comes towards you, the y-axis increases to the | ||
+ | right, and the z-axis increases " | ||
+ | the origin. | ||
+ | |||
+ | To rotate the object we derived a 3x3 matrix for each axis which describes a | ||
+ | rotation about that axis. After rotating we translate the object along the | ||
+ | z-axis and then project it through the origin onto a plane z=constant. | ||
+ | |||
+ | As you recall the projection of a point is done by drawing a line from the | ||
+ | point through the origin, and then figuring out where this line intersects | ||
+ | our plane z=constant. | ||
+ | off the object and through our little pinhole camera lens. | ||
+ | |||
+ | Clearly for any solid object some parts of the object will remain hidden, | ||
+ | though, i.e. when you look at your monitor you can't see the back of it, and | ||
+ | you probably can't see the sides. | ||
+ | mathematically? | ||
+ | |||
+ | |||
+ | Hidden Surfaces | ||
+ | --------------- | ||
+ | |||
+ | Imagine our object with some light shining on it -- when will a part of the | ||
+ | object be hidden? | ||
+ | never reaches our eyes, which happens whenever a part of the object is | ||
+ | " | ||
+ | flat plate, like your hand (you also might think of a cube). | ||
+ | rod sticking out of the plate, exactly perpendicular to the plate (take your | ||
+ | index finger from your other hand, and touch it to your palm at a | ||
+ | ninety-degree angle). | ||
+ | bouncing off and heading towards your eyes. | ||
+ | |||
+ | No matter where you place your hand in space, the very last point at which it | ||
+ | is visible is when it is exactly parallel to the light rays coming from it to | ||
+ | your eyes; or, to put it another way, when the light rays are exactly | ||
+ | perpendicular to a normal vector to the surface (in the above case this | ||
+ | vector is either a rod or your finger). | ||
+ | a light ray is less than ninety degrees, then the surface is visible. | ||
+ | greater, then the surface is invisible. | ||
+ | |||
+ | At this point you may be wondering how to figure out the angle between two | ||
+ | vectors. | ||
+ | we use a very important tool in our mathematical toolbox, the dot product. | ||
+ | |||
+ | If we have two vectors v1=(x1, | ||
+ | is defined to be | ||
+ | |||
+ | v1 dot v2 = x1*y1 + x2*y2 + x3*y3 | ||
+ | |||
+ | note that this is a _scalar_ (i.e. a number), and not a vector. | ||
+ | show that | ||
+ | |||
+ | v1 dot v2 = |v1|*|v2|*cos(theta) | ||
+ | |||
+ | where | | denotes length and theta is the angle between the two vectors. | ||
+ | Since cos(theta) is positive or negative depending on whether or not theta is | ||
+ | less than or greater than ninety degrees, all we have to do is take the dot | ||
+ | product and look at the sign. | ||
+ | |||
+ | But we need to understand something about the dot-product. | ||
+ | angle between two vectors joined at their base; mathematically the way we are | ||
+ | going to draw the light ray is to draw a line FROM the origin TO a point on | ||
+ | the surface. | ||
+ | to the palm of your hand and then slide the normal vector down this line | ||
+ | until the base of the normal vector touches your eye. | ||
+ | |||
+ | The whole point of this is that when we look at the dot product we need to | ||
+ | keep in mind that if the dot product is negative, the face is visible. | ||
+ | |||
+ | All that remains is to write down an equation: let's say that we've rotated | ||
+ | the surface and know a point P=(x,y,z) on the rotated surface, and we have a | ||
+ | normal vector to the surface vn=(vx, | ||
+ | the z-axis so that P -> (x,y,z-z0) = P - (0, | ||
+ | product we find that | ||
+ | |||
+ | P' dot vn = (P dot vn) - z0*vz | ||
+ | |||
+ | But (P dot vn) is simply a constant: because these are rigid rotations the | ||
+ | length of P never changes, presumably the length of vn never changes, and the | ||
+ | angle between the two never changes. | ||
+ | |||
+ | K = (P dot vn)/z0 | ||
+ | |||
+ | so that all we need to do is subtract the z-component of the normal vector | ||
+ | from K and check if it is positive or negative: if negative, the face is | ||
+ | visible. | ||
+ | (0,0,z0)) we simply add the two together. | ||
+ | |||
+ | We seem to have left something out here: how do we calculate the normal | ||
+ | vector vn? One way to do it is by using another vector operator, the | ||
+ | cross-product. | ||
+ | cross product of two vectors is another vector, perpendicular to the first | ||
+ | two. | ||
+ | |||
+ | The most common way to visualize the cross-product is by using your right | ||
+ | hand: imagine two vectors in space, and place your right hand along one of | ||
+ | them, with your thumb sticking out. Now curl your fingers towards the other | ||
+ | vector. | ||
+ | cross-product of the first two. You can easily convince yourself then that | ||
+ | (A x B) = -(B x A), that is, if you reverse the order of the cross product, | ||
+ | you get a vector pointing in the opposite direction. | ||
+ | |||
+ | Therefore, if we take any two vectors in the face (in particular, we know the | ||
+ | edge of the face), and then take their cross-product, | ||
+ | vector. | ||
+ | |||
+ | But because we are dealing with a cube, we have an even easier method! | ||
+ | can use the fact that the faces on a cube are perpendicular to each other: if | ||
+ | we take two points and subtract them we get a vector going between the two | ||
+ | points. | ||
+ | " | ||
+ | two z-coordinates, | ||
+ | |||
+ | This is how the program does it, and the specifics will be explained later. | ||
+ | Right now I want to show you a second method of hidden surface detection. | ||
+ | Instead of using the three-dimensional rotate vectors, what if we use the | ||
+ | two-dimensional _projected_ vectors? | ||
+ | these vectors we get a vector which either points into the screen or out of | ||
+ | it, which corresponds to a positive or a negative result. | ||
+ | |||
+ | The cross-product is usually done by taking the determinant of a matrix. | ||
+ | am not going to explain that here -- you can look in any decent calculus book | ||
+ | for the full cross-product. | ||
+ | the vector, and the z-coordinate of v1 x v2 is: | ||
+ | |||
+ | v1x*v2y - v1y*v2x | ||
+ | |||
+ | Whether or not the face is visible depends on how you define v1 and v2! | ||
+ | Always remember that (v1 x v2) = -(v2 x v1). | ||
+ | |||
+ | What is this quantity anyways? | ||
+ | vectors v1 and v2. The magnitude of the cross-product just happens to be | ||
+ | |||
+ | |v1|*|v2|*sin(theta) | ||
+ | |||
+ | which you can easily see is the area of a parallelogram with sides v1 and v2. | ||
+ | For this reason the second method apparently goes by the name SAM -- Signed | ||
+ | Area Method. | ||
+ | product in a similar way). | ||
+ | |||
+ | Note that the second method is quite general, while the first method only | ||
+ | works for objects which have perpendicular surfaces (at least, in it's | ||
+ | current form presented here). | ||
+ | significantly faster. | ||
+ | |||
+ | Now that we've hidden the faces, it's time to fill them: | ||
+ | |||
+ | |||
+ | Filled Faces | ||
+ | ------------ | ||
+ | |||
+ | Q: How do you make a statue of an elephant? | ||
+ | A: Start with a block of granite and carve away everything | ||
+ | that doesn' | ||
+ | |||
+ | The first method of filling faces is very simple in concept. | ||
+ | want a cube with white faces and black edges. | ||
+ | the buffer black and then draw in the white edges. | ||
+ | the entire buffer white, draw the edges in black, and then make everything | ||
+ | outside of the edges black. | ||
+ | then take away everything that doesn' | ||
+ | of it like a cookie cutter: we press our cube-shaped cutter down and remove | ||
+ | all the dough outside of the cutter. | ||
+ | |||
+ | This simplistic method actually has some advantages. | ||
+ | large, we spend very little time doing the actual un-filling. We don't care | ||
+ | about how complicated the object is, because we just trace out the edge. | ||
+ | Finally, this gives us an extremely easy way of implementing a rudimentary | ||
+ | texture-mapping in multicolor mode. For instance, instead of coloring the | ||
+ | block white, what if we used a changing pattern of colors? | ||
+ | edge is a specific color, the pattern can be any combination of the other | ||
+ | three colors. | ||
+ | that the inititalization program needs to be changed slightly to run this | ||
+ | program. | ||
+ | |||
+ | In other words, we roll the dough, draw a pattern into it, press our cutter | ||
+ | down and remove the outside dough. | ||
+ | over it. | ||
+ | |||
+ | On the downside it's not quite so easy to do things like have each face a | ||
+ | separate color (but who wants wimpy separate colors when you can have | ||
+ | evolving texture patterns, eh? :). | ||
+ | |||
+ | The program makes a few refinements to this technique. | ||
+ | of coloring the entire buffer white, it calculates ahead of time the minimum | ||
+ | and maximum values for y, and only colors that part of the drawing area | ||
+ | white. | ||
+ | |||
+ | For the sake of completeness, | ||
+ | exclusive-or. A clever way of filling faces is to use some EOR magic. | ||
+ | say we want to fill everything between two points A and B. We want to start | ||
+ | filling at point A and stop at point B, and since EOR is a bit flipper this | ||
+ | gives us a means of filling. | ||
+ | |||
+ | 00010000 | ||
+ | 00000000 | ||
+ | 00000000 | ||
+ | 00010000 | ||
+ | |||
+ | Now consider the following little piece of code: | ||
+ | |||
+ | LDA #00 | ||
+ | EOR A | ||
+ | STA A | ||
+ | EOR A+1 | ||
+ | STA A+1 | ||
+ | EOR A+2 | ||
+ | STA A+2 | ||
+ | EOR A+3 ;point B | ||
+ | |||
+ | The result is: | ||
+ | |||
+ | 00010000 | ||
+ | 00010000 | ||
+ | 00010000 | ||
+ | 00010000 | ||
+ | |||
+ | This is the conceptual idea behind an EOR-buffer. | ||
+ | can't just implement this as-is. | ||
+ | worry about now. Try EORing a vertical line. What about when two lines | ||
+ | share a single pixel at their intersection? | ||
+ | |||
+ | Ah reckon y'all will just have to wait until next time tuh see :). | ||
+ | |||
+ | |||
+ | Da Program | ||
+ | ---------- | ||
+ | |||
+ | Let's review the code. We check to see if we should increase or decrease the | ||
+ | rotation rate (or quit), and then update the angles. Next we calculate the | ||
+ | rotation matrix using a table of sines and cosines. Then we rotate and | ||
+ | project the points by using a table of d/(z/64-z0) values. Somewhere in there | ||
+ | we clear a working buffer, draw all of the lines, swap the buffers, pass Go, | ||
+ | collect $200 (actually, considering where the buffers are located we either | ||
+ | collect $300 or $380 :), and go around the loop again. | ||
+ | |||
+ | First, some bugs. There were two places in the line drawing routine where an | ||
+ | SBC was performed with the carry clear when it should have been set, so we | ||
+ | need to add some SECs in there. | ||
+ | y-rotations, | ||
+ | |||
+ | Although not a bug, there is something to think about. | ||
+ | increases to the right, and y-increases downwards, with z coming out of the | ||
+ | screen. | ||
+ | calculations were performed in a right-handed coordinate system. | ||
+ | means is that one of our coordinates is actually a mirror-image of what it | ||
+ | should be, while the other coordinate is where it is supposed to be. | ||
+ | Remember that a projection generates a negative mirror-image of the object -- | ||
+ | the computer coordinate system mirrors a single axis of the image again! | ||
+ | |||
+ | Because of the symmetry of a cube, this makes no difference. A smart way to | ||
+ | fix this is to translate the object in _front_ of the projection plane, i.e. | ||
+ | to use the translation z=z+c instead of the currently used z=z-c, but still | ||
+ | project through the origin and into the plane z=1. Since I am not | ||
+ | particularly smart though, not to mention lazy and unmotivated, | ||
+ | bother to fix this. | ||
+ | |||
+ | Before we start adding the new stuff like hidden surfaces into the code, why | ||
+ | don't we think about doing some simple optimizations to the old code? One | ||
+ | really easy thing to fix is in the projection routine. | ||
+ | the earlier program rotated z and then added 128 to it to use as an index. | ||
+ | Why bother to add 128 at all? I dunno -- sometimes things seem like a good | ||
+ | idea at the time. So that's something to fix. It's not that it's a big waste | ||
+ | of time, it's just one of those annoying things that there' | ||
+ | |||
+ | How about the variables? | ||
+ | -- why not move them all into zero page? Sounds good to me! We just need to | ||
+ | make sure we don't use any sensitive locations in zero page that will hose | ||
+ | the whole computer. | ||
+ | |||
+ | On the C64 an interrupt is performed every 60th of a second which scans the | ||
+ | keyboard and things like that -- why in the world do we want that running in | ||
+ | the middle of all our calculations? | ||
+ | it back on before checking to see if F1 etc. was pressed!). | ||
+ | |||
+ | A footnote observation: | ||
+ | are used (MUL2 and DIV2) which multiply and divide a signed number by two. | ||
+ | It never ceases to amaze me what happens when you simply sit down and think | ||
+ | about something, and in this case these two macros can be made much simpler: | ||
+ | |||
+ | MUL2 ASL ; | ||
+ | |||
+ | DIV2 CLC | ||
+ | BPL :POS | ||
+ | SEC | ||
+ | :POS ROR | ||
+ | |||
+ | These two routines will multiply/ | ||
+ | by two (note that the source included with this article uses the old method). | ||
+ | |||
+ | There' | ||
+ | rotation is pretty straightforward -- nah, skip that. The line drawing | ||
+ | routine takes up an awful lot of time -- maybe we can speed that up? That's | ||
+ | for a future article :). Clearing the buffer takes a lot of time, but now | ||
+ | that we're going to have filled faces there isn't too much we can do about | ||
+ | that. In fact, so much more time is spent in those two areas than is spent | ||
+ | in other parts of the code that any other optimizations we make really aren't | ||
+ | going to make a very big difference... BUT... | ||
+ | |||
+ | How about multiplications? | ||
+ | |||
+ | |||
+ | Fast Signed Multiply | ||
+ | -------------------- | ||
+ | |||
+ | Ah, now here is something we can fix. Consider the following function: | ||
+ | |||
+ | f(x) = x*x/4 | ||
+ | |||
+ | Now notice that | ||
+ | |||
+ | f(a+b) - f(a-b) = a*b | ||
+ | |||
+ | Wowsers! | ||
+ | multiplication in no time! | ||
+ | |||
+ | Whoa there, wait a minute, all of our calculations are using signed numbers. | ||
+ | Won't that make a difference? | ||
+ | general -- I never said what the sign of a and b are. The fact that we are | ||
+ | using two's complement notation makes this even simpler! | ||
+ | |||
+ | Recall that our multiplication is | ||
+ | |||
+ | x -> x * [d/ | ||
+ | |||
+ | where x is a signed floating point number multiplied by 64 (that is, instead | ||
+ | of going from -1 to 1 x would go from -64 to 64). Previously we made d | ||
+ | large, so that the table of d/(z-z0) would be more accurate. Then we | ||
+ | multiplied the numbers together and divided by 64, a procedure which took | ||
+ | between 150 and 180 cycles, leaving us with a signed, 8-bit result. | ||
+ | |||
+ | Well, that's easy to duplicate. | ||
+ | |||
+ | (a*b)/64 = [ f(a+b) - f(a-b) ]/64 | ||
+ | = g(a+b) - g(a-b) | ||
+ | where | ||
+ | g(x) = x*x/256. | ||
+ | |||
+ | In other words, if we modify our table slightly, we get exactly the result we | ||
+ | want. So here is the code to multiply two numbers together: | ||
+ | |||
+ | * A*Y -> A Signed, 8-bit result | ||
+ | |||
+ | STA ZP1 ;ZP1 -- zero page pointer to table of g(x) | ||
+ | EOR #$FF | ||
+ | CLC | ||
+ | ADC #$01 | ||
+ | STA ZP2 ;ZP2 also points to g(x) | ||
+ | LDA (ZP1), | ||
+ | SEC | ||
+ | SBC (ZP2), | ||
+ | |||
+ | And that's it -- we're done. The above takes 24-26 cycles to execute -- not | ||
+ | bad at all! Yes, with another table we could make it even faster, but this | ||
+ | is good enough for us. | ||
+ | |||
+ | At the moment we don't do very many multiplications, | ||
+ | we write a generalized routine to rotate and project an arbitrary object, | ||
+ | this will give us a humongous time savings. | ||
+ | |||
+ | Astute readers may be thinking ahead here: in the program, for each | ||
+ | projection we have two multiplications, | ||
+ | in both cases. | ||
+ | multiplication even more efficient, right? | ||
+ | being extremely careful, for reasons that will be detailed in exactly two | ||
+ | paragraphs. | ||
+ | |||
+ | BUT WAIT! We have to think about a few things here. What happens when we | ||
+ | multiply, say, -1 * -1. In two's complement notation -1 is equal to 255. So | ||
+ | our above algorithm adds 255 to 255 to get an index into the table and | ||
+ | gets... oops! Our table needs to be larger than 256 bytes! | ||
+ | very easy to fix, because of the way two's complement works. | ||
+ | do is put an exact copy of the 256 byte table on top of itself, and the table | ||
+ | will work fine. (If you look at the initialization program you will notice | ||
+ | that the statement is: q%=s*s:poke bm+j, | ||
+ | |||
+ | BUT WAIT!!! | ||
+ | vertices start out at the points (+/ | ||
+ | to moving these points around on a sphere, so it is easy to see that the | ||
+ | largest rotated value we can have is sqr(3), the radius of this sphere. | ||
+ | Since we are multiplying these numbers by 64, the largest value we can have | ||
+ | for these numbers is 64*sqr(3) = 111. Okay, no big whoop, what are the | ||
+ | values for d/(z0-z/64) anyways? | ||
+ | like 28... | ||
+ | |||
+ | ACK! When we go to multiply we are going to add 111 to 28 and get 137, but | ||
+ | in two's complement notation this is equal to -119. | ||
+ | |||
+ | Example: | ||
+ | a=28 | ||
+ | b=111 | ||
+ | f(b+a) = f(137) = f(-119) in two' | ||
+ | f(b-a) = f(83) | ||
+ | |||
+ | In our table lookup we really want 137*137 but we are going to get -119*-119! | ||
+ | One option is to never choose d very large so that we don't have this | ||
+ | problem, but the solution turns out to be much simpler, again due to the way | ||
+ | two's complementing works. | ||
+ | |||
+ | We can see that we can get numbers larger than 127 when we add the two | ||
+ | multiplicands together. | ||
+ | Certainly the smallest x is going to get is -111. Ahhh... d/(z0-z/64) is | ||
+ | always positive, so when we add them together we will get something larger | ||
+ | than -111, which in two's complement notation is 145. This means that we can | ||
+ | treat the table entries between 127 and at least 145 as positive numbers | ||
+ | instead of two's complement negative numbers, and everything will work out | ||
+ | great. | ||
+ | |||
+ | Incidentally, | ||
+ | special effects. | ||
+ | following line: | ||
+ | |||
+ | [for j=0 to 255] | ||
+ | 290 S=J:IF S>150 THEN S=256-S | ||
+ | [poke bm+j,S*S] | ||
+ | |||
+ | Try changing the inequality from S>150 to S>120 (or S>127, where it would be | ||
+ | for a two's complement table), and see what happens! | ||
+ | |||
+ | And this is why we can't store d/(z0-z) in the pointers ZP1 and ZP2 -- if we | ||
+ | did, then for a given multiplication we could get numbers larger than 127 and | ||
+ | smaller than -128, and our clever table would no longer work right. | ||
+ | still get around this -- all we need is two clever tables, one for the | ||
+ | positive d/(z0-z) and one for negative d/ | ||
+ | have the situation outlined above: no numbers smaller than -90 or so, and | ||
+ | numbers possible larger than 127. For the second table we have the reverse | ||
+ | situation: no numbers larger than 90 or so, but possible numbers less than | ||
+ | -128. Since we are using two pointers anyways (ZP1 and ZP2), this is not | ||
+ | difficult to implement. | ||
+ | |||
+ | The end result is that you can do the entire projection in around 36 cycles | ||
+ | if you so desire. | ||
+ | something like EOR #$FF; CLC; ADC #$01. Well, if we set up the second table | ||
+ | as f(x)=(x+1)^2/ | ||
+ | so the instructions can be removed. | ||
+ | |||
+ | ... (rotate z) | ||
+ | STA ZP1 | ||
+ | EOR #$FF | ||
+ | STA ZP2 | ||
+ | ... (rotate x) | ||
+ | TAY | ||
+ | LDA (ZP1),Y | ||
+ | SEC | ||
+ | SBC (ZP2), | ||
+ | ... (rotate y) | ||
+ | TAY | ||
+ | LDA (ZP1),Y | ||
+ | SEC | ||
+ | SBC (ZP2),Y | ||
+ | |||
+ | Looks like 36-40 cycles to me! The program doesn' | ||
+ | uses a single table, and repeats the STA ZP1 stuff at each step. A few | ||
+ | cycles wasted won't kill us (there are plenty of cycles wasted in the code), | ||
+ | and it is probably tricky enough to follow as it is. | ||
+ | |||
+ | You might be asking, what is the true minimum value for a given z0 and d? | ||
+ | Well, I tried writing down a set of equations and minimizing according to | ||
+ | some constraints, | ||
+ | write little newton-iteration solver for. In other words, I don't think you | ||
+ | can write down a simple function of d and z0 to give the table boundaries. | ||
+ | found 150 to be a perfectly reasonable number to use. | ||
+ | |||
+ | Incidentally, | ||
+ | want to go fiddling around with it again. | ||
+ | |||
+ | ONE MORE THING!!! | ||
+ | boundary for the above algorithm to work correctly. | ||
+ | bit by. | ||
+ | |||
+ | Logarithmic Multiplication | ||
+ | -------------------------- | ||
+ | |||
+ | As long as we're talking about fast multiplication here, it is worthwhile to | ||
+ | mention another method for multiplying two numbers together. | ||
+ | it you need to understand two important properties of logarithms: | ||
+ | |||
+ | log(x*y) = log(x) + log(y) | ||
+ | log_b(x) = y < | ||
+ | |||
+ | These are a reflection of the fact that logarithms are inverses of | ||
+ | exponentiation. | ||
+ | together is to take their logs, add, and then exponentiate the result. | ||
+ | you could have a table of log_2 (base 2 logarithms) and another table of 2^x, | ||
+ | and do a multiplication very quickly. (Actually, you'd want a table of | ||
+ | 32*log_2(x), | ||
+ | |||
+ | First, dealing with signed numbers is much trickier -- the logarithm of a | ||
+ | negative number is a complex (i.e. real+imaginary) number, complete with | ||
+ | branch cuts. You can get around this by setting up the tables in a special | ||
+ | way (for instance by letting log(-x)=-log(x)) and putting in some special | ||
+ | handling, but it isn't as efficient as the algorithm used in the program. | ||
+ | |||
+ | Second, accuracy decreases significantly as x and y get large, so that for an | ||
+ | eight-bit table of logarithms you will often get an answer that is off by one | ||
+ | or more. You can in fact get around this problem by using some sneaky | ||
+ | manipulation -- if you are interested in seeing this, contact us! | ||
+ | |||
+ | But it is worthwhile to keep this method in mind if you need a really fast | ||
+ | multiplication and you aren't too worried about accuracy. | ||
+ | |||
+ | Christopher Jam (phillips@ee.uwa.edu.au) has come up with an interesting | ||
+ | variation on this method. | ||
+ | different structure for the signed numbers, and runs almost as fast as the | ||
+ | method used by the program -- contact him for more information if you're | ||
+ | interested. | ||
+ | |||
+ | |||
+ | Hidden Surfaces | ||
+ | --------------- | ||
+ | |||
+ | The remainder of this follows right from the discussion section. In the | ||
+ | program the cube vertices are labeled as | ||
+ | |||
+ | P1 = 1,1,1 | ||
+ | P2 = 1,-1,1 | ||
+ | P3 = -1,-1,1 | ||
+ | P4 = -1,1,1 | ||
+ | P5 = 1,1,-1 | ||
+ | P6 = 1,-1,-1 | ||
+ | P7 = -1,-1,-1 | ||
+ | P8 = -1,1,-1 | ||
+ | |||
+ | and the faces are chosen to be | ||
+ | |||
+ | Face 1: P1 P2 P3 P4 | ||
+ | 6: P5 P6 P7 P8 | ||
+ | Face 2: P1 P2 P5 P6 | ||
+ | 5: P3 P4 P7 P8 | ||
+ | Face 3: P1 P4 P8 P5 | ||
+ | 4: P2 P3 P6 P7 | ||
+ | |||
+ | (think of it as a six-sided dice, with six opposite of one, etc.). | ||
+ | vectors are then | ||
+ | |||
+ | Face 1: P1-P5 | ||
+ | Face 2: P1-P4 | ||
+ | Face 3: P1-P2 | ||
+ | |||
+ | This means that we need to store the z-coordinates for points 1,2,4, and 5. | ||
+ | Note that the opposite faces have exactly opposite normal vectors, so that | ||
+ | for instance the normal vector for face 6 is P5-P1, the negative of face 1. | ||
+ | |||
+ | Here is something to consider: when one face is visible, the opposite face | ||
+ | cannot be visible! | ||
+ | is not true; it is entirely possible to have two opposite faces invisible. | ||
+ | To prove this to yourself just look at your favorite box, like your monitor, | ||
+ | straight-on, | ||
+ | |||
+ | All that the program does is subtract z-coordinates and add them to the | ||
+ | constant K, and check the sign. Unfortunately we can have a positive | ||
+ | overflow while adding stuff together (since these are signed numbers), and if | ||
+ | we don't catch the positive overflow we will calculate a negative result when | ||
+ | the result is actually positive! | ||
+ | surface removal. | ||
+ | |||
+ | |||
+ | Filled Faces | ||
+ | ------------ | ||
+ | |||
+ | The program currently uses the first algorithm to fill faces, i.e. the | ||
+ | cookie-cutter elephant-carving method. | ||
+ | checks each value of y to find the minimum and maximum values for this plot, | ||
+ | ymin and ymax. The program then clears the buffer up to and including ymin, | ||
+ | fills the buffer from ymin+1 to ymax-1, and then clears the rest of the | ||
+ | buffer. | ||
+ | happen on those lines is an edge -- there is no point in filling these lines | ||
+ | and then clearing them, since they will always be clear. | ||
+ | buffer between ymin and ymax, we save some time in removing the junk from the | ||
+ | edges of the cube. | ||
+ | |||
+ | Next, the cube is drawn. | ||
+ | i.e. our fill color is white. | ||
+ | black. | ||
+ | line routine as-is, but of course being the lazy programmer I am I decided | ||
+ | instead to change the table BITP. You may recall that the earlier table had | ||
+ | entries like %10000000 %01000000 etc. Now it has entries like %01111111 | ||
+ | %10111111 etc., and instead of ORAing the values into the buffer, they are | ||
+ | ANDed into the buffer. | ||
+ | is solid ones. | ||
+ | |||
+ | Finally, to un-fill the outside of the cube the program simply goes through | ||
+ | the buffer from ymin to ymax, coloring everything black until it hits a zero, | ||
+ | i.e. an edge. At this point it calculates the appropriate pattern to clear | ||
+ | up to the edge, and then does the same thing starting from the right hand | ||
+ | side of the buffer. | ||
+ | everything black until it hits the edge of the cube, and does this for all | ||
+ | the relevant y-values. | ||
+ | |||
+ | |||
+ | Texture Mapping | ||
+ | --------------- | ||
+ | |||
+ | More of a fill-pattern really. | ||
+ | but in multicolor mode. Now instead of using a solid color to fill the | ||
+ | buffer the program uses a series of colored lines -- really a very simple | ||
+ | pattern. | ||
+ | pattern buffer, and to copy that into the drawing buffer. | ||
+ | try are colored squares which shift around. | ||
+ | quick hack, but at least it demonstrates the concept. | ||
+ | |||
+ | MAKE SURE that you change the value of D from 170 to 85 if you try this | ||
+ | program! | ||
+ | located at line 240 in INIT3D2.0 | ||
+ | |||
+ | |||
+ | Memory Map | ||
+ | ---------- | ||
+ | |||
+ | The main program is located at $8000=32768 and is 3200 bytes long. | ||
+ | |||
+ | $8000-$8C00 - Program | ||
+ | $8C00-$8C80 - Bit position table | ||
+ | $8C80-$8D00 - Table of sines | ||
+ | $8D00-$8D80 - Table of cosines. | ||
+ | $8D80-$8E80 - Table of d/(z0-z/64) | ||
+ | $8F00-$9100 - Two 256-byte tables of g(x)=x*x/ | ||
+ | |||
+ | $3000 - First drawing buffer | ||
+ | $3800 - Second drawing buffer | ||
+ | |||
+ | INIT3D is a simple basic program to set up the tables. | ||
+ | important setup routines are: | ||
+ | |||
+ | lines 100-150 - Set up the trigonometric tables | ||
+ | lines 233-310 - Set up the projection and mult tables | ||
+ | 240 - Location of constants D and Z0 | ||
+ | 290 - Set table boundary for multiplication | ||
+ | |||
+ | |||
+ | That's all -- until next time... | ||
+ | |||
+ | Steve Judd George Taylor 12/ | ||
+ | |||
+ | This document is Copyright 1995 by Stephen Judd and George Taylor. Much like | ||
+ | the previous one. It is also freely distributable. | ||
+ | |||
+ | And here is the source code: | ||
+ | |||
+ | |||
+ | ******************************** | ||
+ | * * | ||
+ | * Stephen Judd * | ||
+ | * George Taylor | ||
+ | * Started: 7/ | ||
+ | * Finished: 7/ | ||
+ | * v2.0 Completed: 12/ | ||
+ | * * | ||
+ | * Well, if all goes well this * | ||
+ | * program will rotate a cube. * | ||
+ | * * | ||
+ | * v2.0 + New and Improved! | ||
+ | * Now with faster routines, | ||
+ | * hidden surfaces, filled | ||
+ | * faces, and extra top secret | ||
+ | * text messages! | ||
+ | * * | ||
+ | * This program is intended to * | ||
+ | * accompany the article in * | ||
+ | * C=Hacking, Jan. 95 issue. | ||
+ | * For details on this program, * | ||
+ | * read the article! | ||
+ | * * | ||
+ | * Write to us! * | ||
+ | * * | ||
+ | * Myself when young did * | ||
+ | * eagerly frequent | ||
+ | * Doctor and Saint, and heard * | ||
+ | * great Argument | ||
+ | * About it and about: but * | ||
+ | * evermore | ||
+ | * Came out by the same Door * | ||
+ | * as in I went. * | ||
+ | * - Rubaiyat | ||
+ | * * | ||
+ | * Though I speak with the * | ||
+ | * tongues of men and of angles * | ||
+ | * and have not love, I am * | ||
+ | * become as sounding brass, or * | ||
+ | * a tinkling cymbal. | ||
+ | * - 1 Corinthians 13 * | ||
+ | * * | ||
+ | * P.S. This was written using * | ||
+ | * Merlin 128. * | ||
+ | ******************************** | ||
+ | |||
+ | ORG $8000 | ||
+ | |||
+ | * Constants | ||
+ | |||
+ | BUFF1 EQU $3000 ;First character set | ||
+ | BUFF2 EQU $3800 ;Second character set | ||
+ | BUFFER | ||
+ | X1 EQU $FB ;Points for drawing a line | ||
+ | Y1 EQU $FC ;These zero page addresses | ||
+ | X2 EQU $FD ;don't conflict with BASIC | ||
+ | Y2 EQU $FE | ||
+ | DX EQU $F9 | ||
+ | DY EQU $FA | ||
+ | TEMP1 EQU $FB ;Of course, could conflict with x1 | ||
+ | TEMP2 EQU $FC ;Temporary variables | ||
+ | ZTEMP EQU $02 ;Used for buffer swap. Don't touch. | ||
+ | Z1 EQU $22 ;Used by math routine | ||
+ | Z2 EQU $24 ;Don't touch these either! | ||
+ | K EQU $B6 ;Constant used for hidden | ||
+ | ;surface detection - don't touch | ||
+ | FACES EQU $B5 ;Used in hidden surfaces. | ||
+ | YMIN EQU $F7 ;Used in filled faces -- as | ||
+ | YMAX EQU $F8 ;usual, don't touch | ||
+ | ANGMAX | ||
+ | |||
+ | * VIC | ||
+ | |||
+ | VMCSB EQU $D018 | ||
+ | BKGND EQU $D020 | ||
+ | BORDER | ||
+ | SSTART | ||
+ | |||
+ | |||
+ | * Kernal | ||
+ | |||
+ | CHROUT | ||
+ | GETIN EQU $FFE4 | ||
+ | |||
+ | * Some variables | ||
+ | |||
+ | TX1 = $3F | ||
+ | TY1 = $40 | ||
+ | TX2 = $41 | ||
+ | TY2 = $42 | ||
+ | P1X = $92 ;These are temporary storage | ||
+ | P1Y = $93 ;Used in plotting the projection | ||
+ | P2X = $94 | ||
+ | P2Y = $95 ;They are here so that we | ||
+ | P3X = $96 ;don't have to recalculate them. | ||
+ | P3Y = $AE | ||
+ | P4X = $AF ;They make life easy. | ||
+ | P4Y = $B0 | ||
+ | P5X = $B1 ;Why are you looking at me like that? | ||
+ | P5Y = $B2 ;Don't you trust me? | ||
+ | P6X = $B3 | ||
+ | P6Y = $B4 ;Having another child wasn't my idea. | ||
+ | P7X = $71 | ||
+ | P7Y = $50 | ||
+ | P8X = $51 | ||
+ | P8Y = $52 | ||
+ | P1Z = $57 ;These are z-coordinates | ||
+ | P2Z = $58 ;We only need these four to check | ||
+ | P4Z = $59 ;for hidden faces | ||
+ | P5Z = $60 | ||
+ | DSX = $61 ;DSX is the increment for | ||
+ | ;rotating around x | ||
+ | DSY = $62 ;Similar for DSY, DSZ | ||
+ | DSZ = $63 | ||
+ | SX = $64 ;These are the actual angles in x y and z | ||
+ | SY = $65 | ||
+ | SZ = $66 | ||
+ | T1 = $67 ;These are used in the rotation | ||
+ | T2 = $68 | ||
+ | T3 = $69 ;See the article for more details | ||
+ | T4 = $6A | ||
+ | T5 = $6B | ||
+ | T6 = $6C | ||
+ | T7 = $6D | ||
+ | T8 = $6E | ||
+ | T9 = $6F | ||
+ | T10 = $70 | ||
+ | A11 = $A5 ;These are the elements of the rotation matrix | ||
+ | B12 = $A6 ;XYZ | ||
+ | C13 = $A7 | ||
+ | D21 = $A8 ;The number denotes (row, | ||
+ | E22 = $A9 | ||
+ | F23 = $AA | ||
+ | G31 = $AB | ||
+ | H32 = $AC | ||
+ | I33 = $AD | ||
+ | |||
+ | |||
+ | *** Macros | ||
+ | |||
+ | MOVE MAC | ||
+ | LDA ]1 | ||
+ | STA ]2 | ||
+ | <<< | ||
+ | |||
+ | GETKEY | ||
+ | WAIT JSR GETIN | ||
+ | CMP #00 | ||
+ | BEQ WAIT | ||
+ | <<< | ||
+ | |||
+ | *------------------------------- | ||
+ | |||
+ | LDA #$00 | ||
+ | STA BKGND | ||
+ | STA BORDER | ||
+ | LDA VMCSB | ||
+ | AND # | ||
+ | ORA #%00010000 | ||
+ | STA VMCSB | ||
+ | |||
+ | LDY #00 | ||
+ | LDA #<TTEXT | ||
+ | STA TEMP1 | ||
+ | LDA #>TTEXT | ||
+ | STA TEMP2 | ||
+ | JMP TITLE | ||
+ | TTEXT HEX 9305111111 | ||
+ | TXT ' | ||
+ | TXT ' | ||
+ | HEX 9F ;cyan | ||
+ | TXT ' | ||
+ | HEX 99 | ||
+ | TXT ' | ||
+ | HEX 9B | ||
+ | TXT ' | ||
+ | HEX 96 | ||
+ | TXT ' | ||
+ | HEX 9B | ||
+ | TXT ' for more details!', | ||
+ | HEX 0D1D1D9E12 | ||
+ | TXT ' | ||
+ | TXT ' - inc/dec x-rotation', | ||
+ | HEX 1D1D12 | ||
+ | TXT ' | ||
+ | TXT ' - inc/dec y-rotation', | ||
+ | HEX 1D1D12 | ||
+ | TXT ' | ||
+ | TXT ' - inc/dec z-rotation', | ||
+ | HEX 1D1D12 | ||
+ | TXT ' | ||
+ | TXT ' resets', | ||
+ | TXT ' | ||
+ | HEX 0D05 | ||
+ | TXT ' | ||
+ | HEX 00 | ||
+ | TITLE LDA (TEMP1),Y | ||
+ | BEQ :CONT | ||
+ | JSR CHROUT | ||
+ | INY | ||
+ | BNE TITLE | ||
+ | INC TEMP2 | ||
+ | JMP TITLE | ||
+ | TXT 'This is a secret text message!' | ||
+ | :CONT >>> | ||
+ | |||
+ | **** Set up tables(?) | ||
+ | |||
+ | * Tables are currently set up in BASIC | ||
+ | * and by the assembler. | ||
+ | |||
+ | TABLES | ||
+ | STA Z1+1 | ||
+ | STA Z2+1 | ||
+ | |||
+ | **** Clear screen and set up " | ||
+ | SETUP LDA #$01 ; | ||
+ | STA $D021 ;This is done so that older | ||
+ | LDA #147 ; | ||
+ | JSR CHROUT | ||
+ | LDA #$00 ; | ||
+ | STA $D021 | ||
+ | LDA #<SSTART | ||
+ | ADC #12 ;The goal is to center the graphics | ||
+ | STA TEMP1 ;Column 12 | ||
+ | LDA #> | ||
+ | STA TEMP1+1 | ||
+ | LDA #00 | ||
+ | LDY #00 | ||
+ | LDX #00 ;x will count 16 rows for us | ||
+ | CLC | ||
+ | |||
+ | :LOOP STA (TEMP1),Y | ||
+ | INY | ||
+ | ADC #16 | ||
+ | BCC :LOOP | ||
+ | CLC | ||
+ | LDA TEMP1 | ||
+ | ADC #40 ;Need to add 40 to the base pointer | ||
+ | STA TEMP1 ;To jump to the next row | ||
+ | LDA TEMP1+1 | ||
+ | ADC #00 ;Take care of carries | ||
+ | STA TEMP1+1 | ||
+ | LDY #00 | ||
+ | INX | ||
+ | | ||
+ | CPX #16 | ||
+ | BNE :LOOP ;Need to do it 16 times | ||
+ | |||
+ | **** Set up buffers | ||
+ | |||
+ | LDA #<BUFF1 | ||
+ | STA BUFFER | ||
+ | LDA #>BUFF1 | ||
+ | STA BUFFER+1 | ||
+ | STA ZTEMP ;ztemp will make life simple for us | ||
+ | LDA VMCSB | ||
+ | AND # | ||
+ | ORA #%00001110 | ||
+ | STA VMCSB | ||
+ | |||
+ | **** Set up initial values | ||
+ | |||
+ | INIT LDA #00 | ||
+ | STA DSX | ||
+ | STA DSY | ||
+ | STA DSZ | ||
+ | STA SX | ||
+ | STA SY | ||
+ | STA SZ | ||
+ | |||
+ | *------------------------------- | ||
+ | * Main loop | ||
+ | |||
+ | **** Get keypress | ||
+ | |||
+ | MAIN | ||
+ | CLI | ||
+ | KPRESS | ||
+ | CMP #133 ;F1? | ||
+ | BNE :F2 | ||
+ | LDA DSX | ||
+ | CMP # | ||
+ | BEQ :CONT | ||
+ | INC DSX ;otherwise increase x-rotation | ||
+ | JMP :CONT | ||
+ | :F2 CMP #137 ;F2? | ||
+ | BNE :F3 | ||
+ | LDA DSX | ||
+ | BEQ :CONT | ||
+ | DEC DSX | ||
+ | JMP :CONT | ||
+ | :F3 CMP #134 | ||
+ | BNE :F4 | ||
+ | LDA DSY | ||
+ | CMP #ANGMAX/2 | ||
+ | BEQ :CONT | ||
+ | INC DSY ;Increase y-rotation | ||
+ | JMP :CONT | ||
+ | :F4 CMP #138 | ||
+ | BNE :F5 | ||
+ | LDA DSY | ||
+ | BEQ :CONT | ||
+ | DEC DSY | ||
+ | JMP :CONT | ||
+ | :F5 CMP #135 | ||
+ | BNE :F6 | ||
+ | LDA DSZ | ||
+ | CMP #ANGMAX/2 | ||
+ | BEQ :CONT | ||
+ | INC DSZ ;z-rotation | ||
+ | JMP :CONT | ||
+ | :F6 CMP #139 | ||
+ | BNE :F7 | ||
+ | LDA DSZ | ||
+ | BEQ :CONT | ||
+ | DEC DSZ | ||
+ | JMP :CONT | ||
+ | :F7 CMP #136 | ||
+ | BNE :Q | ||
+ | JMP INIT | ||
+ | :Q CMP #' | ||
+ | BNE :CONT | ||
+ | JMP CLEANUP | ||
+ | |||
+ | :CONT SEI ;Speed things up a bit | ||
+ | |||
+ | **** Update angles | ||
+ | |||
+ | UPDATE | ||
+ | LDA SX | ||
+ | ADC DSX | ||
+ | CMP # | ||
+ | BCC :CONT1 | ||
+ | SBC #ANGMAX :If so, reset | ||
+ | : | ||
+ | CLC | ||
+ | LDA SY | ||
+ | ADC DSY | ||
+ | CMP #ANGMAX | ||
+ | BCC :CONT2 | ||
+ | SBC # | ||
+ | : | ||
+ | CLC | ||
+ | LDA SZ | ||
+ | ADC DSZ | ||
+ | CMP #ANGMAX | ||
+ | BCC :CONT3 | ||
+ | SBC #ANGMAX | ||
+ | : | ||
+ | |||
+ | **** Rotate coordinates | ||
+ | |||
+ | ROTATE | ||
+ | |||
+ | *** First, calculate t1, | ||
+ | |||
+ | ** Two macros to simplify our life | ||
+ | ADDA | ||
+ | CLC | ||
+ | LDA ]1 | ||
+ | ADC ]2 | ||
+ | * Use two trig tables to remove the below CMP etc. code | ||
+ | CMP # | ||
+ | BCC DONE | ||
+ | SBC # | ||
+ | DONE <<< | ||
+ | |||
+ | SUBA | ||
+ | SEC | ||
+ | LDA ]1 | ||
+ | SBC ]2 | ||
+ | BCS DONE | ||
+ | ADC # | ||
+ | DONE <<< | ||
+ | |||
+ | ** Now calculate t1,t2,etc. | ||
+ | |||
+ | >>> | ||
+ | STA T1 ; | ||
+ | >>> | ||
+ | STA T2 ; | ||
+ | >>> | ||
+ | STA T3 ; | ||
+ | >>> | ||
+ | STA T4 ; | ||
+ | >>> | ||
+ | STA T5 ; | ||
+ | >>> | ||
+ | STA T6 ; | ||
+ | >>> | ||
+ | STA T7 ; | ||
+ | >>> | ||
+ | STA T8 ; | ||
+ | >>> | ||
+ | STA T9 ; | ||
+ | >>> | ||
+ | STA T10 ;t10=sx+sy | ||
+ | |||
+ | * Et voila! | ||
+ | |||
+ | *** Next, calculate A,B,C,...,I | ||
+ | |||
+ | ** Another useful little macro | ||
+ | DIV2 | ||
+ | ;It is assumed that the number | ||
+ | BPL POS ;is in the accumulator | ||
+ | CLC | ||
+ | EOR #$FF ;We need to un-negative the number | ||
+ | ADC #01 ;by taking it's complement | ||
+ | | ||
+ | CLC | ||
+ | EOR #$FF | ||
+ | ADC #01 ;Make it negative again | ||
+ | JMP DONEDIV | ||
+ | POS LSR ;Number is positive | ||
+ | DONEDIV | ||
+ | |||
+ | MUL2 | ||
+ | BPL POSM | ||
+ | CLC | ||
+ | EOR #$FF | ||
+ | ADC #$01 | ||
+ | ASL | ||
+ | CLC | ||
+ | EOR #$FF | ||
+ | ADC #$01 | ||
+ | JMP DONEMUL | ||
+ | POSM ASL | ||
+ | DONEMUL | ||
+ | |||
+ | ** Note that we are currently making a minor leap | ||
+ | ** of faith that no overflows will occur. | ||
+ | |||
+ | : | ||
+ | LDX T1 | ||
+ | LDA COS,X | ||
+ | LDX T2 | ||
+ | ADC COS,X | ||
+ | STA A11 ; | ||
+ | : | ||
+ | LDA SIN,X | ||
+ | SEC | ||
+ | LDX T2 | ||
+ | SBC SIN,X | ||
+ | STA B12 ; | ||
+ | : | ||
+ | LDA SIN,X | ||
+ | >>> | ||
+ | STA C13 ;C=sin(sy) | ||
+ | : | ||
+ | LDX T8 | ||
+ | LDA COS,X | ||
+ | LDX T7 | ||
+ | SBC COS,X | ||
+ | SEC | ||
+ | LDX T5 | ||
+ | SBC COS,X | ||
+ | CLC | ||
+ | LDX T6 | ||
+ | ADC COS,X ; | ||
+ | >>> | ||
+ | CLC | ||
+ | LDX T3 | ||
+ | ADC SIN,X | ||
+ | SEC | ||
+ | LDX T4 | ||
+ | SBC SIN,X | ||
+ | STA D21 ; | ||
+ | : | ||
+ | LDX T5 | ||
+ | LDA SIN,X | ||
+ | LDX T6 | ||
+ | SBC SIN,X | ||
+ | SEC | ||
+ | LDX T7 | ||
+ | SBC SIN,X | ||
+ | SEC | ||
+ | LDX T8 | ||
+ | SBC SIN,X ; | ||
+ | >>> | ||
+ | CLC | ||
+ | LDX T3 | ||
+ | ADC COS,X | ||
+ | CLC | ||
+ | LDX T4 | ||
+ | ADC COS,X | ||
+ | STA E22 ; | ||
+ | : | ||
+ | LDA SIN,X | ||
+ | SEC | ||
+ | LDX T10 | ||
+ | SBC SIN,X | ||
+ | STA F23 ; | ||
+ | : | ||
+ | LDA SIN,X | ||
+ | SEC | ||
+ | LDX T8 | ||
+ | SBC SIN,X | ||
+ | SEC | ||
+ | LDX T7 | ||
+ | SBC SIN,X | ||
+ | SEC | ||
+ | LDX T5 | ||
+ | SBC SIN,X ; | ||
+ | >>> | ||
+ | CLC | ||
+ | LDX T4 | ||
+ | ADC COS,X | ||
+ | SEC | ||
+ | LDX T3 | ||
+ | SBC COS,X | ||
+ | STA G31 ; | ||
+ | : | ||
+ | LDX T6 | ||
+ | LDA COS,X | ||
+ | LDX T7 | ||
+ | ADC COS,X | ||
+ | SEC | ||
+ | LDX T5 | ||
+ | SBC COS,X | ||
+ | SEC | ||
+ | LDX T8 | ||
+ | SBC COS,X ; | ||
+ | >>> | ||
+ | CLC | ||
+ | LDX T3 | ||
+ | ADC SIN,X | ||
+ | CLC | ||
+ | LDX T4 | ||
+ | ADC SIN,X | ||
+ | STA H32 ; | ||
+ | :WHEW CLC | ||
+ | LDX T9 | ||
+ | LDA COS,X | ||
+ | LDX T10 | ||
+ | ADC COS,X | ||
+ | STA I33 ; | ||
+ | |||
+ | ** It's all downhill from here. | ||
+ | JMP DOWNHILL | ||
+ | TXT 'Gee Brain, what do you want to do ' | ||
+ | TXT ' | ||
+ | |||
+ | ** Rotate, project, and store the points | ||
+ | DOWNHILL | ||
+ | |||
+ | * A neat macro | ||
+ | NEG MAC ;Change the sign of a two's complement | ||
+ | CLC | ||
+ | LDA ]1 ; | ||
+ | EOR #$FF | ||
+ | ADC #$01 | ||
+ | <<< | ||
+ | |||
+ | *------------------------------- | ||
+ | * These macros replace the previous projection | ||
+ | * subroutine. | ||
+ | |||
+ | SMULT MAC ;Multiply two signed 8-bit | ||
+ | ;numbers: A*Y/64 -> A | ||
+ | STA Z1 | ||
+ | CLC | ||
+ | EOR #$FF | ||
+ | ADC #$01 | ||
+ | STA Z2 | ||
+ | LDA (Z1),Y | ||
+ | SEC | ||
+ | SBC (Z2),Y | ||
+ | <<< | ||
+ | |||
+ | |||
+ | ADDSUB | ||
+ | ;depending on first input | ||
+ | IF -=]1 ;If subtract | ||
+ | | ||
+ | SBC ]2 | ||
+ | | ||
+ | CLC | ||
+ | ADC ]2 | ||
+ | FIN | ||
+ | <<< | ||
+ | |||
+ | |||
+ | PROJECT | ||
+ | ;two inputs are used (x,y) | ||
+ | ; | ||
+ | ;The third input is used to | ||
+ | ;determine if the rotated | ||
+ | ; | ||
+ | ;stored, and if so where. | ||
+ | ;The calling routine handles | ||
+ | ;changing the sign of z. | ||
+ | |||
+ | LDA I33 ;Calculate rotated z: | ||
+ | >>> | ||
+ | >>> | ||
+ | IF P,]3 ;Do we need to store the point? | ||
+ | STA ]3 ;Then do so! | ||
+ | FIN | ||
+ | * EOR #128 ;We are going to take 128+z | ||
+ | | ||
+ | LDA ZDIV, | ||
+ | | ||
+ | |||
+ | LDA C13 ;Now calculate rotated x | ||
+ | >>> | ||
+ | >>> | ||
+ | >>> | ||
+ | CLC | ||
+ | ADC #64 ;Offset the coordinate | ||
+ | | ||
+ | |||
+ | LDA F23 ;Now it's y's turn | ||
+ | >>> | ||
+ | >>> | ||
+ | >>> | ||
+ | CLC | ||
+ | ADC #64 ;Offset | ||
+ | CMP YMIN ; | ||
+ | BCS NOTMIN | ||
+ | STA YMIN | ||
+ | BCC NOTMAX | ||
+ | NOTMIN | ||
+ | BCC NOTMAX | ||
+ | STA YMAX | ||
+ | NOTMAX | ||
+ | |||
+ | <<< | ||
+ | |||
+ | |||
+ | LDA #64 ;Reset Ymin and Ymax | ||
+ | STA YMIN | ||
+ | STA YMAX | ||
+ | |||
+ | * P1=[1 1 1] | ||
+ | >>> | ||
+ | STX P1X | ||
+ | STY P1Y | ||
+ | * P2=[1 -1 1] | ||
+ | >>> | ||
+ | STX P2X | ||
+ | STY P2Y | ||
+ | * P3=[-1 -1 1] | ||
+ | >>> | ||
+ | STX P3X | ||
+ | STY P3Y | ||
+ | * P4=[-1 1 1] | ||
+ | >>> | ||
+ | STX P4X | ||
+ | STY P4Y | ||
+ | * P8=[-1 1 -1] | ||
+ | >>> | ||
+ | STA C13 | ||
+ | >>> | ||
+ | STA F23 | ||
+ | >>> | ||
+ | STA I33 | ||
+ | >>> | ||
+ | STX P8X | ||
+ | STY P8Y | ||
+ | * P7=[-1 -1 -1] | ||
+ | >>> | ||
+ | STX P7X | ||
+ | STY P7Y | ||
+ | * P6=[1 -1 -1] | ||
+ | >>> | ||
+ | STX P6X | ||
+ | STY P6Y | ||
+ | * P5=[1 1 -1] | ||
+ | >>> | ||
+ | STX P5X | ||
+ | STY P5Y | ||
+ | |||
+ | * A little macro | ||
+ | |||
+ | SETBUF | ||
+ | LDA #00 | ||
+ | STA BUFFER | ||
+ | LDA ZTEMP ;ztemp contains the high byte here | ||
+ | STA BUFFER+1 | ||
+ | <<< | ||
+ | |||
+ | **** Clear buffer | ||
+ | |||
+ | * >>> | ||
+ | *CLRBUF LDA #$00 ;Pretty straightforward, | ||
+ | * LDX #$08 ;I think | ||
+ | * LDY #$00 | ||
+ | *:LOOP STA (BUFFER),Y | ||
+ | * INY | ||
+ | * BNE :LOOP | ||
+ | * INC BUFFER+1 | ||
+ | * DEX | ||
+ | * BNE :LOOP | ||
+ | |||
+ | * This is the new and improved buffer clear | ||
+ | * routine for filled faces | ||
+ | |||
+ | >>> | ||
+ | STA TEMP1+1 | ||
+ | LDA #$80 ; | ||
+ | STA TEMP1 ;Makes life faster for us | ||
+ | FILCLR | ||
+ | LDX #$08 ; | ||
+ | LDY #$00 | ||
+ | : | ||
+ | STA (TEMP1),Y | ||
+ | INY | ||
+ | CPY YMIN | ||
+ | BNE :LOOP1 | ||
+ | LDA #$FF ;Now load with fills | ||
+ | : | ||
+ | STA (TEMP1),Y | ||
+ | INY | ||
+ | CPY YMAX | ||
+ | BCC :LOOP2 | ||
+ | LDA #$00 ; | ||
+ | : | ||
+ | STA (TEMP1),Y | ||
+ | INY | ||
+ | BPL : | ||
+ | LDY #00 | ||
+ | INC BUFFER+1 | ||
+ | INC TEMP1+1 | ||
+ | DEX | ||
+ | BNE : | ||
+ | |||
+ | **** Now draw the lines. | ||
+ | **** But first check for hidden faces! | ||
+ | **** Remember: P1=[1 1 1] P2=[1 -1 1] P3=[-1 -1 1] | ||
+ | **** P4=[-1 1 1] P5=[1 1 -1] P6=[1 -1 -1] P7=[-1 -1 -1] | ||
+ | **** P8=[-1 1 -1] | ||
+ | |||
+ | LINES LDA #00 | ||
+ | STA FACES ;Hidden face counter | ||
+ | : | ||
+ | SEC | ||
+ | SBC P1Z | ||
+ | BVS : | ||
+ | CLC | ||
+ | ADC P5Z ;Is k-v1z < 0? | ||
+ | ;If not, face is invisible | ||
+ | BVC : | ||
+ | LDA P5Z ;Was overflow pos or neg? | ||
+ | : | ||
+ | |||
+ | LDA #$01 ; | ||
+ | STA FACES ;face! | ||
+ | |||
+ | LDA P1X | ||
+ | STA TX1 | ||
+ | LDA P1Y | ||
+ | STA TY1 | ||
+ | LDA P2X | ||
+ | STA TX2 | ||
+ | LDA P2Y | ||
+ | STA TY2 | ||
+ | JSR DRAW ; | ||
+ | |||
+ | LDA P3X | ||
+ | STA TX1 | ||
+ | LDA P3Y | ||
+ | STA TY1 | ||
+ | JSR DRAW ; | ||
+ | |||
+ | LDA P4X | ||
+ | STA TX2 | ||
+ | LDA P4Y | ||
+ | STA TY2 | ||
+ | JSR DRAW ; | ||
+ | |||
+ | LDA P1X | ||
+ | STA TX1 | ||
+ | LDA P1Y | ||
+ | STA TY1 | ||
+ | JSR DRAW ; | ||
+ | JMP : | ||
+ | ; | ||
+ | : | ||
+ | SEC | ||
+ | SBC P5Z | ||
+ | BVS :FACE2 | ||
+ | CLC | ||
+ | ADC P1Z ;Now check if K-v6z < 0 | ||
+ | BVC : | ||
+ | LDA P1Z | ||
+ | : | ||
+ | |||
+ | LDA #$20 | ||
+ | STA FACES ;Otherwise, draw it | ||
+ | |||
+ | LDA P5X | ||
+ | STA TX2 | ||
+ | LDA P5Y | ||
+ | STA TY2 | ||
+ | LDA P6X | ||
+ | STA TX1 | ||
+ | LDA P6Y | ||
+ | STA TY1 | ||
+ | JSR DRAW ; | ||
+ | |||
+ | LDA P7X | ||
+ | STA TX2 | ||
+ | LDA P7Y | ||
+ | STA TY2 | ||
+ | JSR DRAW ; | ||
+ | |||
+ | LDA P8X | ||
+ | STA TX1 | ||
+ | LDA P8Y | ||
+ | STA TY1 | ||
+ | JSR DRAW ; | ||
+ | |||
+ | LDA P5X | ||
+ | STA TX2 | ||
+ | LDA P5Y | ||
+ | STA TY2 | ||
+ | JSR DRAW ; | ||
+ | |||
+ | : | ||
+ | SEC | ||
+ | SBC P1Z | ||
+ | BVS :FACE5 | ||
+ | CLC | ||
+ | ADC P4Z ;K-v2z < 0? | ||
+ | BVC :DRAW2 | ||
+ | LDA P4Z | ||
+ | : | ||
+ | LDA #$02 ;If so, draw it! | ||
+ | ORA FACES | ||
+ | STA FACES | ||
+ | |||
+ | LDX P1X ;We're doing this this way | ||
+ | STX TX1 ;to save a few cycles | ||
+ | LDX P1Y | ||
+ | STX TY1 | ||
+ | |||
+ | AND #$01 ; | ||
+ | BNE :F2S2 ;Skip to next edge if present | ||
+ | |||
+ | LDA P2X | ||
+ | STA TX2 | ||
+ | LDA P2Y | ||
+ | STA TY2 | ||
+ | JSR DRAW ; | ||
+ | |||
+ | :F2S2 LDX P5X | ||
+ | STX TX2 | ||
+ | LDX P5Y | ||
+ | STX TY2 | ||
+ | JSR DRAW ; | ||
+ | |||
+ | LDX P6X | ||
+ | STX TX1 | ||
+ | LDX P6Y | ||
+ | STX TY1 | ||
+ | |||
+ | LDA FACES | ||
+ | AND #$20 ;Also shares an edge with 6 | ||
+ | BNE :F2S4 | ||
+ | |||
+ | JSR DRAW ; | ||
+ | |||
+ | :F2S4 LDA P2X | ||
+ | STA TX2 | ||
+ | LDA P2Y | ||
+ | STA TY2 ;Such is face 2 | ||
+ | JSR DRAW ; | ||
+ | JMP : | ||
+ | |||
+ | : | ||
+ | SEC | ||
+ | SBC P4Z | ||
+ | BVS :FACE3 | ||
+ | CLC | ||
+ | ADC P1Z ;Same thing again... | ||
+ | BVC :DRAW5 | ||
+ | LDA P1Z | ||
+ | : | ||
+ | LDA #$10 | ||
+ | ORA FACES | ||
+ | STA FACES | ||
+ | |||
+ | LDX P3X | ||
+ | STX TX1 | ||
+ | LDX P3Y | ||
+ | STX TY1 | ||
+ | |||
+ | AND #$01 ; | ||
+ | BNE :F5S2 | ||
+ | |||
+ | LDA P4X | ||
+ | STA TX2 | ||
+ | LDA P4Y | ||
+ | STA TY2 | ||
+ | JSR DRAW ; | ||
+ | |||
+ | :F5S2 LDA P7X | ||
+ | STA TX2 | ||
+ | LDA P7Y | ||
+ | STA TY2 | ||
+ | JSR DRAW ; | ||
+ | |||
+ | LDA P8X | ||
+ | STA TX1 | ||
+ | LDA P8Y | ||
+ | STA TY1 | ||
+ | |||
+ | LDA FACES | ||
+ | AND #$20 ; | ||
+ | BNE :F5S4 | ||
+ | |||
+ | JSR DRAW ; | ||
+ | :F5S4 LDA P4X | ||
+ | STA TX2 | ||
+ | LDA P4Y | ||
+ | STA TY2 ;P8-P4 | ||
+ | JSR DRAW ;Two more to go! | ||
+ | |||
+ | : | ||
+ | SEC | ||
+ | SBC P1Z | ||
+ | BVS :FACE4 | ||
+ | CLC | ||
+ | ADC P2Z | ||
+ | BVC :DRAW3 | ||
+ | LDA P2Z | ||
+ | : | ||
+ | LDA #$04 | ||
+ | ORA FACES | ||
+ | STA FACES | ||
+ | |||
+ | LDX P1X | ||
+ | STX TX1 | ||
+ | LDX P1Y | ||
+ | STX TY1 | ||
+ | |||
+ | AND #$01 ; | ||
+ | BNE :F3S2 | ||
+ | |||
+ | LDA P4X | ||
+ | STA TX2 | ||
+ | LDA P4Y | ||
+ | STA TY2 | ||
+ | JSR DRAW ; | ||
+ | |||
+ | :F3S2 LDX P5X | ||
+ | STX TX2 | ||
+ | LDX P5Y | ||
+ | STX TY2 | ||
+ | |||
+ | LDA FACES | ||
+ | AND #$02 ; | ||
+ | BNE :F3S3 | ||
+ | |||
+ | JSR DRAW ; | ||
+ | :F3S3 LDX P8X | ||
+ | STX TX1 | ||
+ | LDX P8Y | ||
+ | STX TY1 | ||
+ | |||
+ | LDA FACES | ||
+ | AND #$20 ; | ||
+ | BNE :F3S4 | ||
+ | |||
+ | JSR DRAW ; | ||
+ | :F3S4 LDX P4X | ||
+ | STX TX2 | ||
+ | LDX P4Y | ||
+ | STX TY2 | ||
+ | |||
+ | LDA FACES | ||
+ | AND #$10 ; | ||
+ | BNE FACEDONE | ||
+ | |||
+ | JSR DRAW ; | ||
+ | JMP FACEDONE | ||
+ | |||
+ | : | ||
+ | SEC | ||
+ | SBC P2Z | ||
+ | BVS FACEDONE | ||
+ | CLC | ||
+ | ADC P1Z | ||
+ | BVC :DRAW4 | ||
+ | LDA P1Z | ||
+ | : | ||
+ | |||
+ | LDA P2X | ||
+ | STA TX1 | ||
+ | LDA P2Y | ||
+ | STA TY1 | ||
+ | |||
+ | LDA FACES | ||
+ | AND #$01 ; | ||
+ | BNE :F4S2 | ||
+ | |||
+ | LDA P3X | ||
+ | STA TX2 | ||
+ | LDA P3Y | ||
+ | STA TY2 | ||
+ | JSR DRAW ; | ||
+ | |||
+ | :F4S2 LDA P6X | ||
+ | STA TX2 | ||
+ | LDA P6Y | ||
+ | STA TY2 | ||
+ | |||
+ | LDA FACES | ||
+ | AND #$02 ; | ||
+ | BNE :F4S3 | ||
+ | |||
+ | JSR DRAW ; | ||
+ | :F4S3 LDA P7X | ||
+ | STA TX1 | ||
+ | LDA P7Y | ||
+ | STA TY1 | ||
+ | |||
+ | LDA FACES | ||
+ | AND #$20 ; | ||
+ | BNE :F4S4 | ||
+ | |||
+ | JSR DRAW ; | ||
+ | :F4S4 LDA P3X | ||
+ | STA TX2 | ||
+ | LDA P3Y | ||
+ | STA TY2 | ||
+ | |||
+ | LDA FACES | ||
+ | AND #$10 ; | ||
+ | BNE FACEDONE | ||
+ | |||
+ | JSR DRAW ; | ||
+ | FACEDONE | ||
+ | |||
+ | **** Now we need to unfill the outside from the faces | ||
+ | UNFILL | ||
+ | :LOOP >>> | ||
+ | LDX #08 | ||
+ | :L1 LDA (BUFFER),Y | ||
+ | EOR #$FF ;Go till we find a plotted | ||
+ | BNE : | ||
+ | * LDA #00 ;Unfilling as we go... | ||
+ | STA (BUFFER),Y | ||
+ | LDA #$80 | ||
+ | STA BUFFER | ||
+ | LDA (BUFFER),Y | ||
+ | EOR #$FF | ||
+ | BNE :GOTCHA | ||
+ | * LDA #00 | ||
+ | STA (BUFFER),Y | ||
+ | STA BUFFER | ||
+ | INC BUFFER+1 | ||
+ | | ||
+ | BNE :L1 ;Really shouldn' | ||
+ | JSR CHOKE | ||
+ | JMP SWAPBUF | ||
+ | |||
+ | : | ||
+ | STA TEMP1 ;Now find the high bit | ||
+ | LDA #00 | ||
+ | :L2 SEC | ||
+ | ROL | ||
+ | LSR TEMP1 ;Should really use a table | ||
+ | BNE :L2 ;for this! | ||
+ | AND (BUFFER),Y | ||
+ | STA (BUFFER),Y | ||
+ | |||
+ | LDA ZTEMP ;Now go to the end | ||
+ | ;Carry is clear | ||
+ | ;Actually we add 7 | ||
+ | ADC #$06 ;16 columns of 128 bytes | ||
+ | STA BUFFER+1 | ||
+ | LDA #$80 | ||
+ | STA BUFFER | ||
+ | : | ||
+ | EOR #$FF | ||
+ | BNE :GOTCHA2 | ||
+ | STA (BUFFER),Y | ||
+ | STA BUFFER | ||
+ | LDA (BUFFER),Y | ||
+ | EOR #$FF | ||
+ | BNE :GOTCHA2 | ||
+ | STA (BUFFER),Y | ||
+ | LDA #$80 | ||
+ | STA BUFFER | ||
+ | DEC BUFFER+1 | ||
+ | BNE :LOOP2 | ||
+ | |||
+ | :GOTCHA2 STA TEMP1 ;Again find the high bit | ||
+ | LDA #00 | ||
+ | :L3 SEC | ||
+ | ROR | ||
+ | ASL TEMP1 | ||
+ | BNE :L3 | ||
+ | AND (BUFFER),Y | ||
+ | STA (BUFFER),Y | ||
+ | |||
+ | | ||
+ | CPY YMAX | ||
+ | BCC :LOOP ;Until we hit ymax! | ||
+ | BEQ :LOOP ;We need the last one too. | ||
+ | |||
+ | **** Swap buffers | ||
+ | |||
+ | SWAPBUF | ||
+ | EOR #$02 ; | ||
+ | STA VMCSB | ||
+ | LDA #$08 | ||
+ | EOR ZTEMP ;ztemp=high byte just flips | ||
+ | STA ZTEMP ;between $30 and $38 | ||
+ | |||
+ | JMP MAIN ; | ||
+ | |||
+ | TXT 'Same thing we do every night, Pinky: ' | ||
+ | TXT 'try to take over the world!' | ||
+ | |||
+ | |||
+ | *------------------------------- | ||
+ | * General questionable-value error procedure | ||
+ | |||
+ | CHOKE LDX #00 | ||
+ | :LOOP LDA :CTEXT,X | ||
+ | BEQ :DONE | ||
+ | JSR CHROUT | ||
+ | INX | ||
+ | JMP :LOOP | ||
+ | :DONE RTS | ||
+ | : | ||
+ | TXT ' | ||
+ | HEX 0D00 | ||
+ | |||
+ | TXT ' | ||
+ | |||
+ | *------------------------------- | ||
+ | * Drawin' | ||
+ | |||
+ | *** Some useful macros | ||
+ | |||
+ | PLOTPX | ||
+ | | ||
+ | LDA BITP, | ||
+ | BMI C1 | ||
+ | LDA #$80 ; | ||
+ | EOR BUFFER | ||
+ | STA BUFFER | ||
+ | BMI C2 | ||
+ | INC BUFFER+1 | ||
+ | C2 LDA # | ||
+ | C1 AND (BUFFER), | ||
+ | STA (BUFFER),Y | ||
+ | | ||
+ | <<< | ||
+ | |||
+ | PLOTPY | ||
+ | | ||
+ | LDA BITP, | ||
+ | AND (BUFFER),Y | ||
+ | STA (BUFFER),Y | ||
+ | PLA | ||
+ | <<< | ||
+ | |||
+ | CINIT MAC ;Macro to initialize the counter | ||
+ | LDA ]1 ;dx or dy | ||
+ | LSR | ||
+ | EOR #$FF ;(Not really two's complement) | ||
+ | ADC #$01 ;A = 256-dx/2 or 256-dy/2 | ||
+ | <<< | ||
+ | |||
+ | XSTEP MAC ;Macro to take a step in X | ||
+ | XLOOP INX | ||
+ | ADC DY | ||
+ | BCC L1 | ||
+ | * Do we use INY or DEY here? | ||
+ | IF I,]1 ;If the first character is an ' | ||
+ | INY | ||
+ | ELSE | ||
+ | DEY | ||
+ | FIN | ||
+ | SBC DX | ||
+ | L1 >>> | ||
+ | CPX X2 | ||
+ | BNE XLOOP | ||
+ | <<< | ||
+ | |||
+ | YSTEP MAC ;Same thing, but for Y | ||
+ | YLOOP IF I,]1 | ||
+ | INY | ||
+ | ELSE | ||
+ | DEY | ||
+ | | ||
+ | FIN | ||
+ | ADC DX | ||
+ | BCC L2 | ||
+ | | ||
+ | SBC DY | ||
+ | >>> | ||
+ | JMP L3 | ||
+ | L2 >>> | ||
+ | L3 CPY Y2 | ||
+ | BNE YLOOP | ||
+ | <<< | ||
+ | |||
+ | **** Initial line setup | ||
+ | |||
+ | DRAW >>> | ||
+ | >>> | ||
+ | >>> | ||
+ | >>> | ||
+ | >>> | ||
+ | |||
+ | | ||
+ | LDA X2 | ||
+ | SBC X1 | ||
+ | BCS :CONT | ||
+ | LDA Y2 ;If not, swap P1 and P2 | ||
+ | LDY Y1 | ||
+ | STA Y1 | ||
+ | STY Y2 | ||
+ | LDA X1 | ||
+ | LDY X2 | ||
+ | STY X1 | ||
+ | STA X2 | ||
+ | |||
+ | SEC | ||
+ | SBC X1 ;Now A=dx | ||
+ | :CONT STA DX | ||
+ | LDX X1 ;Put x1 into X, now we can trash X1 | ||
+ | |||
+ | COLUMN | ||
+ | | ||
+ | | ||
+ | | ||
+ | LSR | ||
+ | BCC :EVEN ;With a possible extra 128 byte block | ||
+ | LDY #$80 ;if so, set the high bit | ||
+ | STY BUFFER | ||
+ | CLC | ||
+ | :EVEN ADC BUFFER+1 | ||
+ | STA BUFFER+1 | ||
+ | |||
+ | SEC | ||
+ | LDA Y2 ; | ||
+ | SBC Y1 | ||
+ | BCS : | ||
+ | EOR #$FF ; | ||
+ | ADC #$01 | ||
+ | : | ||
+ | CMP DX ; | ||
+ | BCS STEPINY | ||
+ | |||
+ | STEPINX | ||
+ | LDA BITP, | ||
+ | AND (BUFFER),Y | ||
+ | STA (BUFFER),Y | ||
+ | >>> | ||
+ | CPY Y2 | ||
+ | BCS XDECY ;Do we step forwards or backwards in Y? | ||
+ | |||
+ | XINCY >>> | ||
+ | RTS | ||
+ | |||
+ | STEPINY | ||
+ | LDA BITP,X | ||
+ | AND (BUFFER),Y | ||
+ | STA (BUFFER),Y | ||
+ | >>> | ||
+ | CPY Y2 | ||
+ | BCS YDECY | ||
+ | |||
+ | YINCY >>> | ||
+ | RTS | ||
+ | |||
+ | XDECY >>> | ||
+ | | ||
+ | |||
+ | YDECY >>> | ||
+ | RTS | ||
+ | |||
+ | |||
+ | *------------------------------- | ||
+ | * Clean up | ||
+ | |||
+ | CLEANUP | ||
+ | AND # | ||
+ | STA VMCSB | ||
+ | |||
+ | | ||
+ | |||
+ | TXT 'Happy Holidays! ' | ||
+ | TXT 'slj 12/94' | ||
+ | |||
+ | *------------------------------- | ||
+ | * Set up bit table | ||
+ | |||
+ | DS ^ ; | ||
+ | ;So that tables start on a page boundary | ||
+ | BITP LUP 16 ;128 Entries for X | ||
+ | DFB %01111111 | ||
+ | DFB %10111111 | ||
+ | DFB %11011111 | ||
+ | DFB %11101111 | ||
+ | DFB %11110111 | ||
+ | DFB %11111011 | ||
+ | DFB %11111101 | ||
+ | DFB %11111110 | ||
+ | --^ | ||
+ | |||
+ | SIN ; | ||
+ | COS EQU SIN+128 | ||
+ | ;Both of these trig tables are | ||
+ | ;currently set up from BASIC | ||
+ | ZDIV EQU COS+128 | ||
+ | TMATH EQU ZDIV+384 | ||
+ | |||
+ | And here are the native C64 files: | ||
+ | |||
+ | |||
+ | begin 600 cube3d2.0.lnx.uu | ||
+ | M`0A;" | ||
+ | M$1$B.IDB(" | ||
+ | M, | ||
+ | M+C`N3Z" | ||
+ | M4`T@, | ||
+ | M+C" | ||
+ | M(# | ||
+ | M```````````````````````````````````````````````````````````` | ||
+ | M```````````````````````````````````````````````````````````` | ||
+ | M```````````````````````````````````````````````````````````` | ||
+ | M```````````````````````````````````````````````````````````` | ||
+ | M``````````````````" | ||
+ | M@9, | ||
+ | M(" | ||
+ | MFR`@0TA%0TL@3U54(%1(12!*04XN(# | ||
+ | M1YL@1D]2($U/ | ||
+ | M4D]4051)3TX-' | ||
+ | M+T8VDB`M($E.0R]$14, | ||
+ | M4U, | ||
+ | ML? | ||
+ | M(.3_R0# | ||
+ | MD? | ||
+ | MC1C0J0" | ||
+ | M84Q7@LF& | ||
+ | M5X+)B]`)I6/ | ||
+ | M965BR7B0`NEXA648I69E8\EXD`+I> | ||
+ | M> | ||
+ | ML`)I> | ||
+ | M9< | ||
+ | M: | ||
+ | M& | ||
+ | MC!`.& | ||
+ | MIFR]@(PXIF[]@(PXIFW]@(PXIFO]@(P0# | ||
+ | MC3BF:? | ||
+ | M3`" | ||
+ | M5TA!5" | ||
+ | MA5> | ||
+ | M_VD!A22Q(CCQ)!AI0, | ||
+ | MC: | ||
+ | ML2(X\208: | ||
+ | M..6FA2(82? | ||
+ | M0, | ||
+ | M& | ||
+ | MA?> | ||
+ | MY: | ||
+ | M(AA)_VD!A22Q(CCQ)!AI0, | ||
+ | M@(VHI:< | ||
+ | M)+$B./ | ||
+ | MI3CEIH4B& | ||
+ | M: | ||
+ | M(AA)_VD!A22Q(CCQ)!AI0*JEJAAEJ!AEJ84B& | ||
+ | M!(7WD`; | ||
+ | MJ? | ||
+ | MI6`0.ZD!A; | ||
+ | M0B`SBJ62A3^EDX5`(# | ||
+ | M0J6SA3^EM(5`(# | ||
+ | MI; | ||
+ | MIK&& | ||
+ | MY5EP31AE5U`" | ||
+ | M0: | ||
+ | M6%`" | ||
+ | MM2D" | ||
+ | MI; | ||
+ | MI; | ||
+ | M, | ||
+ | MN(F%^ZD`." | ||
+ | M@(6CQJ30YH7[J0`X: | ||
+ | M< | ||
+ | M04M%($]615(@5$A%(%=/ | ||
+ | M0TA/ | ||
+ | ML!.E_J3\A? | ||
+ | MY? | ||
+ | MC# | ||
+ | MR& | ||
+ | MT-%@Z& | ||
+ | MZ.7Z2+T`C# | ||
+ | MT" | ||
+ | M```````````````````````````````````````````````````````````` | ||
+ | M```````````````````````````````````````````````````````````` | ||
+ | M````````````````````````````````````? | ||
+ | MW^_W^_W^? | ||
+ | M_G^_W^_W^_W^? | ||
+ | M]_O]_G^_W^_W^_W^? | ||
+ | ML& | ||
+ | MD: | ||
+ | M*BHJ*BHJ*BHJ*BHJ*BHJ*BHJ*@TJH*" | ||
+ | MH*" | ||
+ | M3U)' | ||
+ | M.32@H*" | ||
+ | MH*" | ||
+ | MH*" | ||
+ | MH%=%3$R@5$A)4Z" | ||
+ | M*@TJH*" | ||
+ | M15> | ||
+ | M24Y%4RR@H*" | ||
+ | MH$9!0T53+*!!3D2@15A44D& | ||
+ | M15, | ||
+ | MH*" | ||
+ | M0T]-4$%.6: | ||
+ | M+J`Y-: | ||
+ | M32R@*@TJH%)%042@5$A%H$%25$E# | ||
+ | MH*" | ||
+ | MH*" | ||
+ | M*@TJH& | ||
+ | M1E)%455%3E2@H*" | ||
+ | M1*!(14%21*" | ||
+ | MH*!A0D]55*!)5*!!3D2@04)/ | ||
+ | MH*" | ||
+ | M3U*@H*" | ||
+ | MH" | ||
+ | MH*" | ||
+ | MH*" | ||
+ | M059%H$Y/ | ||
+ | M1Z!" | ||
+ | M*@TJH*" | ||
+ | MH*" | ||
+ | M3J!54TE.1Z" | ||
+ | M*BHJ*BHJ*BHJ*BHJ*BHJ*BHJ*BHJ*BHJ*BHJ*BHJ*@T-(& | ||
+ | M*J!C3TY35$%.5%, | ||
+ | MH%-%5`UB=69F, | ||
+ | M9F9E< | ||
+ | M3DY)3D< | ||
+ | M, | ||
+ | M9F0@.T1/ | ||
+ | M< | ||
+ | M3U5, | ||
+ | M6: | ||
+ | M05`NH*!D3TXG5*!43U5# | ||
+ | M551)3D4-> | ||
+ | M(& | ||
+ | M1: | ||
+ | MH$E.H$A)1$1%3J!355)& | ||
+ | M3$Q%1*!& | ||
+ | M3U5# | ||
+ | M1TQ%4PT-*J!V: | ||
+ | M8F]R9& | ||
+ | M4D5%3J!-14U/ | ||
+ | M9F0R# | ||
+ | M)# | ||
+ | M=$A%4T6@05)%H%1%35!/ | ||
+ | M3J!03$]45$E.1Z!42$6@4%)/ | ||
+ | M(# | ||
+ | M059%H%1/ | ||
+ | M.W1(15F@34%+1: | ||
+ | M6: | ||
+ | M.V1/ | ||
+ | M24Y' | ||
+ | M-WD@/ | ||
+ | MH$%21: | ||
+ | M15-%H$9/ | ||
+ | M# | ||
+ | M4@T@(" | ||
+ | M4J!D< | ||
+ | M04-454%, | ||
+ | M=# | ||
+ | M(" | ||
+ | M3%, | ||
+ | M-F4-=# | ||
+ | M1: | ||
+ | M>' | ||
+ | M4D]7+$-/ | ||
+ | M(# | ||
+ | M73$-(' | ||
+ | M4D534PUW86ET(& | ||
+ | M9& | ||
+ | M05-314U" | ||
+ | M3D2@5T%)5*!43Z!# | ||
+ | M5$-(H$M%60T@8FYE(& | ||
+ | M(", | ||
+ | M86YU< | ||
+ | M(' | ||
+ | M+2TM+2TM+2TM+2TM+0T-(& | ||
+ | M97(-(& | ||
+ | M5$^@, | ||
+ | M(& | ||
+ | M<# | ||
+ | M145.+*!72$E412R@0U)34J!$3@T@=' | ||
+ | MH%8R+C`G+# | ||
+ | M:& | ||
+ | M(' | ||
+ | MH*!# | ||
+ | M(' | ||
+ | M5$%)3%, | ||
+ | M>' | ||
+ | M=" | ||
+ | M(& | ||
+ | M6BU23U1!5$E/ | ||
+ | M)Z!215-%5%, | ||
+ | M(# | ||
+ | M(& | ||
+ | M:' | ||
+ | M=' | ||
+ | M/ | ||
+ | MH$-54E)%3E1, | ||
+ | M0DQ%4BX-# | ||
+ | M# | ||
+ | M(& | ||
+ | M5$A!5*!/ | ||
+ | M< | ||
+ | M82`C/' | ||
+ | M1: | ||
+ | M=" | ||
+ | M# | ||
+ | M4D]74Z!& | ||
+ | M9&, | ||
+ | M145$H%1/ | ||
+ | M.W1/ | ||
+ | M," | ||
+ | M# | ||
+ | M04-415*@3E5-0D52# | ||
+ | M252@, | ||
+ | M4E, | ||
+ | M82!B=69F97(K, | ||
+ | M24U03$6@1D]2H%53# | ||
+ | M5*!(15)%H%-/ | ||
+ | M; | ||
+ | M3*!604Q515, | ||
+ | M(& | ||
+ | M# | ||
+ | M# | ||
+ | M: | ||
+ | M; | ||
+ | M5$A%4E=)4T6@24Y# | ||
+ | M; | ||
+ | M8R!D< | ||
+ | M< | ||
+ | M4T6@62U23U1!5$E/ | ||
+ | M-0T@;& | ||
+ | M(& | ||
+ | M97$@.F-O; | ||
+ | M8VUP(", | ||
+ | M> | ||
+ | M< | ||
+ | M.F-O; | ||
+ | M+"< | ||
+ | M(& | ||
+ | M13\-(& | ||
+ | M; | ||
+ | M# | ||
+ | M=& | ||
+ | M(# | ||
+ | M5$6@0T]/ | ||
+ | M5# | ||
+ | M3$E& | ||
+ | M(& | ||
+ | M4$D_# | ||
+ | M*E!)# | ||
+ | M(' | ||
+ | M; | ||
+ | M3$-53$%41: | ||
+ | M5# | ||
+ | M/ | ||
+ | M< | ||
+ | M.U0U/ | ||
+ | M/ | ||
+ | M.W-X# | ||
+ | M(# | ||
+ | M60T-*J!E5*!63TE, | ||
+ | M+& | ||
+ | M.V1)5DE$1: | ||
+ | M2$%4H%1(1: | ||
+ | M4@T@8VQC# | ||
+ | M3E5-0D52# | ||
+ | M< | ||
+ | M04M%H$E4H$Y%1T%4259%H$%' | ||
+ | M; | ||
+ | M3%1)4$Q9H$& | ||
+ | M; | ||
+ | M,# | ||
+ | MH%1(052@5T6@05)%H$-54E)%3E1, | ||
+ | MH$]& | ||
+ | M8V$@8VQC# | ||
+ | M(' | ||
+ | M# | ||
+ | M.V(]*%-)3BA4, | ||
+ | M+' | ||
+ | M;& | ||
+ | M>" | ||
+ | M0T]3*%0X*2U# | ||
+ | M(& | ||
+ | M+' | ||
+ | M< | ||
+ | M8PT@;& | ||
+ | M.V5)/ | ||
+ | M9& | ||
+ | M8R!C; | ||
+ | M86QC9B!L9' | ||
+ | M+' | ||
+ | M>" | ||
+ | M(& | ||
+ | M23TH4TE.*%0V*2U324XH5# | ||
+ | M=C(-(& | ||
+ | M8V]S+' | ||
+ | M(& | ||
+ | M# | ||
+ | M< | ||
+ | M*2M# | ||
+ | M;& | ||
+ | M82!H, | ||
+ | M>" | ||
+ | M.VD]*$-/ | ||
+ | MH$923TV@2$5212X-(& | ||
+ | M052@1$^@64]5H%=!3E2@5$^@1$^@)PT@=' | ||
+ | M5$%412R@4%)/ | ||
+ | M# | ||
+ | M0: | ||
+ | M(", | ||
+ | M+2TM+2TM+2TM# | ||
+ | MH%!23TI%0U1)3TX-*J!354)23U5424Y%+@T-< | ||
+ | M6: | ||
+ | M< | ||
+ | M*' | ||
+ | M861D< | ||
+ | M1$5014Y$24Y' | ||
+ | M5`T@< | ||
+ | M5$A%4E=)4T6@55-%H%1(25.@0T]$10T@8VQC# | ||
+ | M# | ||
+ | M10T@(" | ||
+ | M1$E.1Z!43Z`H*R\M, | ||
+ | M142@5$\-(" | ||
+ | M1$E.051%H%-(3U5, | ||
+ | M# | ||
+ | MH%1(1: | ||
+ | M6CH-(# | ||
+ | M(& | ||
+ | M3Z!71: | ||
+ | M3Z!33R$-(& | ||
+ | M, | ||
+ | M(' | ||
+ | M24Y3H%!23TI%0U1)3TX-# | ||
+ | M142@6`T@/ | ||
+ | M/ | ||
+ | M(", | ||
+ | M3U1!5$5$H%@A# | ||
+ | M9& | ||
+ | M;&, | ||
+ | M252@25.@00T@8F-S(& | ||
+ | M< | ||
+ | M5$E.1PUN; | ||
+ | M; | ||
+ | M4T%260T-(# | ||
+ | M04Y$H' | ||
+ | M/ | ||
+ | M< | ||
+ | M.RTQ.W`R> | ||
+ | M(' | ||
+ | M(' | ||
+ | M.W`T> | ||
+ | M9RQC, | ||
+ | M+& | ||
+ | M# | ||
+ | M; | ||
+ | M; | ||
+ | M, | ||
+ | M8: | ||
+ | M1: | ||
+ | M> | ||
+ | M(& | ||
+ | M=68-*F-L< | ||
+ | M*J!L9' | ||
+ | M9F9E< | ||
+ | M> | ||
+ | MH$)51D9%4J!# | ||
+ | M/ | ||
+ | M(& | ||
+ | M1D%35$52H$9/ | ||
+ | MH$1/ | ||
+ | M9F9E< | ||
+ | M; | ||
+ | M82`H8G5F9F5R*2QY# | ||
+ | M8V, | ||
+ | M, | ||
+ | M; | ||
+ | M=& | ||
+ | M3D0-# | ||
+ | M2$5# | ||
+ | MH# | ||
+ | MH' | ||
+ | M<# | ||
+ | M3J!& | ||
+ | M< | ||
+ | MH$LM5C%: | ||
+ | M.F1R87< | ||
+ | M=T%3H$]615)& | ||
+ | M1J!03U.@5$A%3J!++58Q6J`^H# | ||
+ | M4D%7H%1(10T@< | ||
+ | M(& | ||
+ | M(' | ||
+ | M(& | ||
+ | M(' | ||
+ | M(& | ||
+ | M(# | ||
+ | M5DE324), | ||
+ | M96, | ||
+ | M14-+H$E& | ||
+ | M3$]7# | ||
+ | M# | ||
+ | M;& | ||
+ | M< | ||
+ | M;& | ||
+ | M.W`V+7`W# | ||
+ | M: | ||
+ | M< | ||
+ | M< | ||
+ | M# | ||
+ | M(R0P, | ||
+ | M(& | ||
+ | M.U1/ | ||
+ | M(", | ||
+ | M.W-+25" | ||
+ | M=' | ||
+ | M(& | ||
+ | M(# | ||
+ | M# | ||
+ | M5$B@-@T@8FYE(# | ||
+ | M<# | ||
+ | M, | ||
+ | M-2!L9& | ||
+ | M, | ||
+ | M.F1R87< | ||
+ | M86-E< | ||
+ | M; | ||
+ | M< | ||
+ | M9C5S, | ||
+ | M9' | ||
+ | M=' | ||
+ | M(# | ||
+ | M># | ||
+ | M4D6@5$^@1T\A# | ||
+ | M86-E-`T@8VQC# | ||
+ | M, | ||
+ | M9& | ||
+ | M=' | ||
+ | MH# | ||
+ | M82!T> | ||
+ | M, | ||
+ | M05)%4Z!7251(H# | ||
+ | M, | ||
+ | M8V5S# | ||
+ | M(& | ||
+ | M(' | ||
+ | M# | ||
+ | M90T-.F9A8V4T(& | ||
+ | M8VQC# | ||
+ | M9F%C961O; | ||
+ | M# | ||
+ | M-', | ||
+ | M(& | ||
+ | M# | ||
+ | M, | ||
+ | M(' | ||
+ | M)# | ||
+ | M<#< | ||
+ | M(& | ||
+ | M9& | ||
+ | M1: | ||
+ | MH$]55%-)1$6@1E)/ | ||
+ | M(# | ||
+ | M(R1F9B`[9T^@5$E, | ||
+ | M.U!/ | ||
+ | M4Z!71: | ||
+ | M9F5R# | ||
+ | M;& | ||
+ | M97(K, | ||
+ | M(# | ||
+ | M87!B=68-# | ||
+ | M# | ||
+ | M;# | ||
+ | M04), | ||
+ | M(" | ||
+ | M(# | ||
+ | M)# | ||
+ | M9& | ||
+ | M1*!73U)+H$)!0TM705)$4R$-(& | ||
+ | M82`H8G5F9F5R*2QY# | ||
+ | M549& | ||
+ | M# | ||
+ | M=69F97(K, | ||
+ | M3J!& | ||
+ | M;" | ||
+ | M*2QY# | ||
+ | M; | ||
+ | M1: | ||
+ | M9& | ||
+ | M; | ||
+ | M4U2@1DQ)4%, | ||
+ | M<" | ||
+ | M1: | ||
+ | MH%1/ | ||
+ | M+2TM+2TM+2TM+2TM+2TM# | ||
+ | M15)23U*@4%)/ | ||
+ | M>' | ||
+ | M9& | ||
+ | M3TM%1*`Z*"< | ||
+ | M+2TM+2TM+2TM+2TM+2TM+2TM+2TM# | ||
+ | M2$Z@3$%(3BX-# | ||
+ | M(# | ||
+ | MH%1)344-(& | ||
+ | M82`C)# | ||
+ | M(# | ||
+ | M8G5F9F5R*S$-8S(@;& | ||
+ | MH$-(04Y' | ||
+ | M142@1D%# | ||
+ | M82$-(# | ||
+ | M3$52H$%.1*!.14-%4U-!4EDA# | ||
+ | M3U6@2E535*!)3D-214%31: | ||
+ | MH$-(04Y' | ||
+ | M(# | ||
+ | M3E1%4@T@;& | ||
+ | M14%, | ||
+ | M+S*@3U*@, | ||
+ | M3$]/ | ||
+ | M4*!)3J!X# | ||
+ | M: | ||
+ | M0U1%4J!)4Z!!3J`G: | ||
+ | M, | ||
+ | M# | ||
+ | MH$9/ | ||
+ | M15)9H$E-4$]25$%.5" | ||
+ | M5T%94Z!)3D-214%31: | ||
+ | M, | ||
+ | M8FYE(' | ||
+ | M(# | ||
+ | M(# | ||
+ | M/ | ||
+ | M.VY/ | ||
+ | M55)%H%@Q/ | ||
+ | M.VE& | ||
+ | M> | ||
+ | M(' | ||
+ | M5$^@>" | ||
+ | M1*!42$6@1DE24U2@0T], | ||
+ | M34%$1: | ||
+ | M0EE41: | ||
+ | MH$), | ||
+ | M0: | ||
+ | M1TB@0DE4# | ||
+ | M1$2@24Z@5$A%H$Y534)%4J!/ | ||
+ | M97(K, | ||
+ | M1%D-(' | ||
+ | M; | ||
+ | M; | ||
+ | M: | ||
+ | M>" | ||
+ | M.W!, | ||
+ | M*2QY# | ||
+ | MH%1(1: | ||
+ | M3U)705)$4Z!/ | ||
+ | M; | ||
+ | M251)3TZ@3D5615*@2%525*!!3EE/ | ||
+ | M9@T@86YD(" | ||
+ | M9' | ||
+ | M=', | ||
+ | M5$A!5`T@< | ||
+ | M97`L9& | ||
+ | M+0TJH& | ||
+ | M4D]-H$)!0TN@24X-(& | ||
+ | M< | ||
+ | M(" | ||
+ | M+0TJH' | ||
+ | M4$%' | ||
+ | M05)9# | ||
+ | M, | ||
+ | M, | ||
+ | M, | ||
+ | M+*`Q, | ||
+ | M# | ||
+ | M5$Q9H%-%5*!54*!& | ||
+ | M24].H%1!0DQ%# | ||
+ | M*%@I/ | ||
+ | M+2TM+0" | ||
+ | M(" | ||
+ | MGR`@(" | ||
+ | M($]55" | ||
+ | M3U)%($1%5$%)3%, | ||
+ | M# | ||
+ | M3D, | ||
+ | M455)5`T-!2`@(" | ||
+ | MT/; | ||
+ | M%M`)$(T6T*F/ | ||
+ | MD? | ||
+ | MD? | ||
+ | MQ*D`A: | ||
+ | MR3SP6.9A3)^" | ||
+ | MQF), | ||
+ | MSXMX& | ||
+ | MY6: | ||
+ | M& | ||
+ | M> | ||
+ | M.*9H_8", | ||
+ | M: | ||
+ | M@(RF;/ | ||
+ | M? | ||
+ | M_VD!2AA)_VD!3!*$2ABF: | ||
+ | MIF[]`(T0# | ||
+ | M`(V%K4R0A& | ||
+ | M1TA4/ | ||
+ | M\208: | ||
+ | MJ(: | ||
+ | MJJ6J&& | ||
+ | MI: | ||
+ | MY: | ||
+ | MK(59JKV`C: | ||
+ | M2? | ||
+ | M2? | ||
+ | ML2(X\208: | ||
+ | M`H7XJ(91A%*EK3CEJSCEK*J]@(VHI:< | ||
+ | M(*JEJCCEJ# | ||
+ | M4*6M&& | ||
+ | M..6IA2(82? | ||
+ | M9: | ||
+ | M& | ||
+ | M@(7[YO> | ||
+ | MH`# | ||
+ | MI96%0B" | ||
+ | M..5@< | ||
+ | M(*> | ||
+ | MA; | ||
+ | MM2D@T`, | ||
+ | MEH8_IJZ& | ||
+ | MT`, | ||
+ | M0" | ||
+ | MT`, | ||
+ | MI96%0*6U*0' | ||
+ | M0*6U*2# | ||
+ | MD: | ||
+ | MJ0, | ||
+ | M`_`# | ||
+ | M& | ||
+ | M+" | ||
+ | M_^A,? | ||
+ | MI4*%_JD`A: | ||
+ | M2DJ0!:" | ||
+ | M`< | ||
+ | MC#& | ||
+ | MHVA, | ||
+ | M,: | ||
+ | M2+T`C#& | ||
+ | M(2!33$H@, | ||
+ | M_# | ||
+ | M/ | ||
+ | MS_/ | ||
+ | M(: | ||
+ | M^X7]..7[A? | ||
+ | M4TQ*(# | ||
+ | M5@@" | ||
+ | M3$53($9/ | ||
+ | M+# | ||
+ | M, | ||
+ | M*B(Z4[(Q`# | ||
+ | M$1$1$1$1$1$1$1$1$1$1$1$1(@!9" | ||
+ | MK" | ||
+ | M*3LZ4[)3JC$Z@@" | ||
+ | M(" | ||
+ | M1" | ||
+ | M*# | ||
+ | M, | ||
+ | M6@!2LK4HNR@Q*: | ||
+ | M22FR4JHU-# | ||
+ | M(D0D(A$1' | ||
+ | MH9T2MIVJG2`B.D, | ||
+ | M3$534TE.1U, | ||
+ | M4B!.24-%/ | ||
+ | M9P"/ | ||
+ | M.D): | ||
+ | MI# | ||
+ | M``H, | ||
+ | MET)3JDDL4SJ70D.J22Q# | ||
+ | M`& | ||
+ | M*%*J, | ||
+ | M0S*J, | ||
+ | M.D, | ||
+ | MCP!.#< | ||
+ | M(2`@(" | ||
+ | M4K*U*+LH," | ||
+ | ML`WF`)=2+# | ||
+ | M#> | ||
+ | M+2TM+2TM+2TM+2TM+2TM+2TM+2TM``D.\`!$LC$W,# | ||
+ | M-C2L, | ||
+ | MIUJR6JLR-38`8`X.`5& | ||
+ | M(*< | ||
+ | M, | ||
+ | M# | ||
+ | M`/ | ||
+ | M5BA2*2D`3@]``9=6*%(I+%(R.I=6*%*J, | ||
+ | M, | ||
+ | MB3, | ||
+ | MK@]> | ||
+ | M(%-*541$0$Y752Y%1%4@(@`& | ||
+ | M+D-!(" | ||
+ | M541$(" | ||
+ | M> | ||
+ | M(" | ||
+ | M, | ||
+ | M,: | ||
+ | M0B0[.H(ZF2(3$2(ZC@```" | ||
+ | MLE*J, | ||
+ | M4R0L0S(L, | ||
+ | M60&/ | ||
+ | M(@# | ||
+ | M" | ||
+ | M1" | ||
+ | M250S1# | ||
+ | M4D4B`+`(, | ||
+ | M($%.1" | ||
+ | M3R!# | ||
+ | M(%%504Y42519($524D]2($I54U0B`# | ||
+ | M$1*> | ||
+ | M4U5212!43R!# | ||
+ | M050@34]35" | ||
+ | M051)3TY3(%53140B```*C`" | ||
+ | M149/ | ||
+ | M($E.($U)3D0@5$A!5" | ||
+ | M5$E03$4@5$E-15, | ||
+ | M$2A04D534R!!3ED@2T59*2([`)\*O@" | ||
+ | M0U5" | ||
+ | M4%!!3$Q)3D=, | ||
+ | M4E0@+2T@250@5T%3($A!0TM%1" | ||
+ | M4U0@34E.551%+" | ||
+ | M($1%5$%)3" | ||
+ | M2T4@5$A%4T4@4%)/ | ||
+ | M(2(````:& | ||
+ | 6& | ||
+ | ` | ||
+ | end | ||
+ | |||
+ | ============================================================================== | ||
+ | </ | ||
+ | ====== 2D Graphics Toolbox -- Circles ====== | ||
+ | < | ||
+ | by Stephen Judd (sjudd@nwu.edu) | ||
+ | |||
+ | 3D is fun and interesting but in the end we must always display our work on a | ||
+ | two-dimensional surface, the CRT. Not only are two-dimensional algorithms | ||
+ | the foundation of three-dimensional drawings, they are of course useful for | ||
+ | many other applications. | ||
+ | complement (hah -- get it?) the 3D articles by George and myself. | ||
+ | the two articles you should have at your disposal a powerful graphics toolbox | ||
+ | for all of your applications. | ||
+ | |||
+ | The foundation of all of our drawings is a single point, and a logical next | ||
+ | step would be a line. Algorithms for doing both of these things were | ||
+ | discussed in depth in the first article of the 3D series, so you can look | ||
+ | those up in a back-issue of C=Hacking. | ||
+ | Curves! | ||
+ | |||
+ | You can write the equation for a circle in many ways, depending on your | ||
+ | coordinate system. | ||
+ | |||
+ | x^2 + y^2 = r^2 (1) | ||
+ | |||
+ | This is a circle centered at the origin (on a computer we can always | ||
+ | translate the points wherever we want), with radius r. In polar coordinates | ||
+ | the equation is r=const. | ||
+ | wazoo, too, but no matter what it looks like we're going to have some | ||
+ | complicated math ahead of us involving multiplications and worse, i.e. | ||
+ | time-intensive computations. | ||
+ | |||
+ | x=r*cos(theta) | ||
+ | y=r*sin(theta) | ||
+ | |||
+ | where theta runs from zero to 2*pi. If we have a table of sines and cosines, | ||
+ | and use the fast multiplication algorithm given in this months 3D article, we | ||
+ | have a routine which gives a good circle in around 50 cycles or so per pixel, | ||
+ | sans plot. | ||
+ | |||
+ | But let's step back and consider: the above equations all give a " | ||
+ | circle. | ||
+ | speed increase? | ||
+ | interesting article otherwise! | ||
+ | |||
+ | Let's say we are standing at a point on the circle, and want to take a step | ||
+ | towards the next point. | ||
+ | line tangent to the circle, touching the point where we are standing. | ||
+ | take a little step in that direction, we ought to get close to the next point | ||
+ | on the circle. The way to calculate the tangent line is to use the | ||
+ | derivative, which will give us the slope of the tangent. | ||
+ | differentials of equation (1) above we have | ||
+ | |||
+ | 2*x dx + 2*y dy = 0 | ||
+ | or | ||
+ | dy/dx = -x/y | ||
+ | |||
+ | Now, if I am at a point (x,y), I want to take a little step in x and a little | ||
+ | step in y: | ||
+ | |||
+ | x = x+dx | ||
+ | y = y+dy | ||
+ | |||
+ | but from the first equation we know that dx = -y/x * dy so that at each step | ||
+ | our iteration is | ||
+ | |||
+ | y = y + dy | ||
+ | x = x - dy*(y/x) | ||
+ | |||
+ | This is the basis of our algorithm. | ||
+ | |||
+ | x = x + dx | ||
+ | y = y - dx*(x/y) | ||
+ | |||
+ | (this is called Euler' | ||
+ | concepts are fundamental to most algorithms for solving differential | ||
+ | equations numerically). | ||
+ | |||
+ | Let's start at the point x=r,y=0 i.e. the right-endpoint of the circle. | ||
+ | Since we are on a computer we want to take a step of length one in some | ||
+ | direction (i.e. step one pixel), and at this point on the circle y is clearly | ||
+ | increasing much faster than x. You may remember from the line drawing | ||
+ | algorithm that we viewed the process as "keep taking steps in x until it is | ||
+ | time to take a step in y". We are going to apply that same philosophy here: | ||
+ | keep taking steps in y until it is time to take a step (backwards) in x. So | ||
+ | our iteration is now: | ||
+ | |||
+ | y_{n+1} = y_n + 1 | ||
+ | x_{n+1} = x_n - y_n/x_n | ||
+ | |||
+ | Hmmm... we have this little problem now of y/x. I certainly don't want to | ||
+ | deal with floating point. | ||
+ | of x as a _discrete_ variable -- as far as a pixel is concerned x is just an | ||
+ | integer, and has no floating point part. So we are going to treat x as a | ||
+ | constant, until it is time to decrease x by one! When does this happen? | ||
+ | Let's expand the x-iteration: | ||
+ | |||
+ | x_{n+1} = x_n - y_n/x_n | ||
+ | = (x_{n-1} - y_{n-1}/ | ||
+ | |||
+ | This is where the magic of using a discrete x comes in. If we make this | ||
+ | assumption of constant x, then x_{n-1} = x_n and the above iteration becomes | ||
+ | |||
+ | x_{n+1} = x_{n-1} - (y_{n-1} + y_n)/ | ||
+ | |||
+ | If we continue this process over an interval where x is constant we get | ||
+ | |||
+ | x_{n+1} = x_0 - (y_n + y_{n-1} + y_{n-2} + ...)/x_0 | ||
+ | |||
+ | When is it time to decrease x? When the sum of the y values at each | ||
+ | iteration exceeds the current x-value the fraction above will be greater than | ||
+ | one, and we will then decrease x. Like I said, we keep x constant, until it | ||
+ | is time to decrease it! :) | ||
+ | |||
+ | How long do we do this for? In the same way that at x=r y increases much | ||
+ | faster than x does, when y=r x increases much faster than y does. Somehwere | ||
+ | in-between they have to be increasing at the same rate, which means the slope | ||
+ | of the tangent line is equal to +/-1, i.e. at the point x=y. At this point | ||
+ | we have drawn one-eighth of the circle. | ||
+ | segments of the circle independentally, | ||
+ | circle and do an 8-way plot. | ||
+ | |||
+ | TO SUMMARIZE: Here is the basic algorithm | ||
+ | |||
+ | x=r | ||
+ | y=0 | ||
+ | a=0 | ||
+ | :loop | ||
+ | y=y+1 | ||
+ | a=a+y | ||
+ | if a>x then x=x-1:a=a-x | ||
+ | plot8(x,y) | ||
+ | :until x<=y | ||
+ | |||
+ | Of course you can refine this in several ways, which will speed things up in | ||
+ | assembly. | ||
+ | becomes | ||
+ | |||
+ | a=a-y | ||
+ | if a<0 then x=x-1:a=a+x | ||
+ | |||
+ | To do the a=a-x you could use a table where f(x)=x, or you might try | ||
+ | something else; here is some code in assembly: | ||
+ | |||
+ | LDX R | ||
+ | LDY #00 | ||
+ | STY Y | ||
+ | TXA | ||
+ | :loop JSR PLOT8 ; | ||
+ | SEC ;Might not need this depending on PLOT8 | ||
+ | INC Y ;Y is in zero page | ||
+ | SBC Y | ||
+ | BCS :loop | ||
+ | DEX | ||
+ | STX X ;X is also in zero-page | ||
+ | ADC X ;Carry is already clear | ||
+ | CPX Y | ||
+ | BCS :loop | ||
+ | JSR PLOT8 ; | ||
+ | |||
+ | Starting at :loop we have 2+3+3+3=11 cycles in the best case and | ||
+ | 2+3+3+2+2+3+3+3+3=24 cycles in the worst case, excluding PLOT8. | ||
+ | course, change the program logic around; if PLOT8 returned with the carry | ||
+ | always clear it would be much smarter to start A as A=256-A and use A=A+Y at | ||
+ | each step instead of A=A-Y. | ||
+ | suggested starting at X=Y(=R/ | ||
+ | removed (I don't know how this affects accuracy, though). | ||
+ | |||
+ | "Yeah, but how well does it work?" | ||
+ | draw a perfect circle for circles with a radius greater than twelve or so. | ||
+ | For circles with a smaller radius the sides start to flatten out, and the | ||
+ | circle becomes squareish. | ||
+ | improves the result over the straight floating-point calculation | ||
+ | significantly! | ||
+ | |||
+ | So this is the best algorithm I was able to come up with for drawing a | ||
+ | circle. | ||
+ | feel free to share them :). As always I must thank George Taylor and | ||
+ | Christopher Jam for their suggestions and for helping me work out some ideas. | ||
+ | |||
+ | If you have any particular 2D algortihms/ | ||
+ | see, please feel free to suggest future topics for the 2D graphics toolbox. | ||
+ | |||
+ | Finally, here is a BASIC7.0 program which demonstrates the algorithm: | ||
+ | |||
+ | 0 REM FAST CIRCLE -- SLJ 9/94 | ||
+ | 10 GRAPHIC 1,1 | ||
+ | 15 REM X=Radius | ||
+ | 20 X=40: | ||
+ | 30 DRAW1, | ||
+ | 40 DRAW1, | ||
+ | 50 DRAW1, | ||
+ | 60 DRAW1, | ||
+ | 70 IF X<=Y THEN 100 | ||
+ | 80 Y=Y+1: | ||
+ | 90 IF TX<0 THEN X=X-1: | ||
+ | 95 GOTO 30 | ||
+ | 100 END | ||
+ | |||
+ | =========================================================================== | ||
+ | </ | ||
+ | ====== AFLI-specs v1.0 ====== | ||
+ | < | ||
+ | by written by D' | ||
+ | |||
+ | Advanged FLI is name I came up with during the time I coded the | ||
+ | first version of AFLI editor. I have never claimed to be the one | ||
+ | who discovered this new graphics mode for 64. I myself give the | ||
+ | credit for COLORFUL/ | ||
+ | before him (splits have been done but in my eyes they don't count). | ||
+ | |||
+ | In AFLI we can get 120 colors in theory (counted like this | ||
+ | 16!/ | ||
+ | each other we get a vision of purple - thanks the television. | ||
+ | |||
+ | AFLI is just like FLI with $08-$0f (hires value) in $d016 and a | ||
+ | couple of sprites over the first three marks. With $d018 we | ||
+ | change the start of screen memory. And the good old $d011 for the | ||
+ | main work. | ||
+ | |||
+ | AFLI is the same as FLI but we don't use the $d800-$dc00 area | ||
+ | for the third color. Actually we can't. In normal hires pictures | ||
+ | the colors on the picture is ordered in a normal screen (normal | ||
+ | text screen is on $0400+). The upper 4 bits is the color for | ||
+ | bit 0 in picture bitmap and the lower 4 bits is the color for bit | ||
+ | 1 in picture bitmap (or the other way...but let us think that was | ||
+ | the right way). | ||
+ | |||
+ | For example: a normal hires picture char (8x8 bits) | ||
+ | |||
+ | | ||
+ | 0 ***** the first spot of the 0bgggggbb | ||
+ | 1*** *** | ||
+ | 2*** *** $68 (blue& | ||
+ | 3******* | ||
+ | 4*** *** like this ----> | ||
+ | 5*** *** | ||
+ | 6*** *** 6gggbgggb | ||
+ | 7 | ||
+ | |||
+ | The bitmap is built just as in a hires picture bit 1 means the pixel | ||
+ | is on and 0 that the pixel is off. | ||
+ | |||
+ | In FLI we have built the screen to have badlines on every scanline of | ||
+ | the screen. This gives us the possibility to change the screenmemory | ||
+ | the picture uses on everyline. Now... when AFLI (and FLI) uses screen | ||
+ | memory for colors and we change the screenmemory start on everyline, | ||
+ | we can have new colors on everyline. | ||
+ | |||
+ | The screens are usually ordered like this. | ||
+ | |||
+ | screen memory used | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | |||
+ | The number of the screen is considered as the number of the line in | ||
+ | 8x8 pixel area. | ||
+ | |||
+ | An example... Here we have cut from the memory showing the first | ||
+ | bytes in every screen. | ||
+ | |||
+ | screen/ | ||
+ | 00 01 02 03 04 05 06 07 08 09 10 11 12 13 ... 39 | ||
+ | $4000 ff ff ff 56 .. .. .. | ||
+ | $4400 ff ff ff 67 .. .. | ||
+ | $4800 ff ff ff 91 .. | ||
+ | $4c00 ff ff ff b3 | ||
+ | $5000 ff ff ff 54 | ||
+ | $5400 ff ff ff 8f | ||
+ | $5800 ff ff ff 54 | ||
+ | $5c00 ff ff ff 10 | ||
+ | |||
+ | Actually the $ff won't have to be there. It will come to the screen | ||
+ | anyway. We have the same ' | ||
+ | $601f). | ||
+ | |||
+ | BITMAP | ||
+ | | ||
+ | 0 ***** | ||
+ | 1*** *** $67 1 77767776 | ||
+ | 2*** *** $91 2 11191119 | ||
+ | 3******* | ||
+ | 4*** *** $54 4 44454445 | ||
+ | 5*** *** $8f 5 fff8fff8 | ||
+ | 6*** *** $54 6 44454445 | ||
+ | 7 | ||
+ | |||
+ | Now the ' | ||
+ | |||
+ | |||
+ | When we code a FLI routine we know that we have succeeded when we get | ||
+ | a 3 marks wide area filled with value $ff on the screen. In FLI the | ||
+ | thing is easily taken away; we just fill the three first bytes of a | ||
+ | line with empty bytes ($00). In AFLI the value $ff is a color. If we | ||
+ | try to clear the three first marks, we still have the gray area. WHY? | ||
+ | The $ff value comes to the screen.. so... the $ff is a color and | ||
+ | the upper four bits of the byte is the color for empty pixels. We can | ||
+ | not clear the first three marks to wipe the thing off. We have a new | ||
+ | lovely problem: we have to put black (or whatever) sprites over that | ||
+ | area. This is just timing. | ||
+ | |||
+ | |||
+ | This may look very complicated and I think you will still be asking | ||
+ | many questions from me - the text I have written surely ain't the | ||
+ | best novel ever written. I'm great in jumping from a thing to | ||
+ | another. | ||
+ | |||
+ | |||
+ | -- | ||
+ | Chief/ | ||
+ | -------------------- | ||
+ | My opinions are not my employers | ||
+ | =========================================================================== | ||
+ | </ | ||
+ | ====== Coding Tricks ====== | ||
+ | < | ||
+ | |||
+ | The following are messages posted to comp.sys.cbm that contain little " | ||
+ | tricks" | ||
+ | to post feel free to email them to me and I'll post them to comp.sys.cbm - | ||
+ | duck@pembvax1.pembroke.edu. | ||
+ | - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - | ||
+ | From: paulvl@python.es.ele.tue.nl (Paul van Loon) | ||
+ | Date: 11 Oct 1994 14:28:49 GMT | ||
+ | |||
+ | Hello everyone, | ||
+ | |||
+ | I really would like to start a thread on your favorite | ||
+ | sequences of 6502 code. I hope much people will react | ||
+ | so we can build up a library of the most beautiful | ||
+ | 6502 code ever seen. | ||
+ | I would suggest little code fragments, probably not | ||
+ | longer than 10 lines, doing some tiny function. | ||
+ | |||
+ | I propose the following standard: | ||
+ | |||
+ | ----------------------- | ||
+ | <xxx> < | ||
+ | < | ||
+ | < | ||
+ | |||
+ | " | ||
+ | ----------------------- | ||
+ | |||
+ | where <xxx> is the symbolic name (3 letters?) you give | ||
+ | to your code sequence, < | ||
+ | " | ||
+ | and <aaa> and on are the 6502 instructions you use. " | ||
+ | is a description of the functionality of your code. The lines | ||
+ | with " | ||
+ | |||
+ | I will hereby start with my all-time favourite code, | ||
+ | I discovered it only recently and I will also | ||
+ | show some examples of how to use it! | ||
+ | |||
+ | --------------------------------------- | ||
+ | |||
+ | B7C "Bit 7 to Carry" | ||
+ | cmp #$80 | ||
+ | |||
+ | This instruction copies bit 7 of A to C | ||
+ | --------------------------------------- | ||
+ | |||
+ | This is really a beauty! It works because | ||
+ | cmp clears the carry if A is below the immediate | ||
+ | value, and sets it if A is higher or same. | ||
+ | All values lower than $80 have bit 7 equal 0, | ||
+ | cmp will clear C for all values below $80, and | ||
+ | thus will ' | ||
+ | equal or above $80 will have bit 7 equal 1, | ||
+ | cmp will set C for all values above $80 and | ||
+ | thus for this case it will also ' | ||
+ | into carry! | ||
+ | |||
+ | ------------------------------------------ | ||
+ | ASR " | ||
+ | B7C | ||
+ | ror | ||
+ | |||
+ | This instruction does a signed divide by 2 | ||
+ | ------------------------------------------ | ||
+ | |||
+ | Again a beauty in my eyes! I have puzzled | ||
+ | many times who to write the on other CPUs | ||
+ | well-known ASR instruction, | ||
+ | seemed to get it implemented without an | ||
+ | extra register to use like: | ||
+ | tax | ||
+ | asl | ||
+ | txa | ||
+ | ror | ||
+ | or even without a branch (figure that out | ||
+ | yourself!). | ||
+ | |||
+ | With these two beauties I want to give you | ||
+ | an idea of what I mean, and please follow me | ||
+ | up!. | ||
+ | |||
+ | BONUS. A little routine to clear the screen | ||
+ | without erasing the sprite pointers. | ||
+ | |||
+ | ----------------------------- | ||
+ | CLS "Clear the screen" | ||
+ | ldx #250 | ||
+ | lda #$20 | ||
+ | clp sta $0400-1, | ||
+ | sta $0400+249,x ;250-499 | ||
+ | sta $0400+499,x ;500-749 | ||
+ | sta $0400+749,x ;750-999 | ||
+ | dex | ||
+ | bne clp | ||
+ | |||
+ | Clear only the screen | ||
+ | ---------------------------- | ||
+ | |||
+ | - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - | ||
+ | From: paulvl@python.es.ele.tue.nl (Paul van Loon) | ||
+ | Subject: Re: Post your favourite little code too! | ||
+ | Date: 20 Oct 1994 12:17:40 GMT | ||
+ | |||
+ | ------------------------------------- | ||
+ | RSL " | ||
+ | B7C | ||
+ | rol | ||
+ | |||
+ | This instruction does a rotation left | ||
+ | through 8 bits (rol does a rotation | ||
+ | left through 9 bits, 8 bits of A | ||
+ | and 1 bit C) | ||
+ | ------------------------------------- | ||
+ | |||
+ | Another useful instruction, | ||
+ | a wraparound rol, e.g. for rotating | ||
+ | bytes in characters to simulate | ||
+ | parallax scrolling. | ||
+ | |||
+ | - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - | ||
+ | |||
+ | A reliable way to create a Straight IRQ? | ||
+ | |||
+ | Well, this is the way I've always used, because it's reliable, flexible | ||
+ | doesn' | ||
+ | depend on D011 etc... | ||
+ | This one only observes the VIC chip: | ||
+ | |||
+ | It's some time since I actually coded a routine of this kind, | ||
+ | and I'm writing this off memory, so there might be some errors in the | ||
+ | following code. | ||
+ | |||
+ | < | ||
+ | of 0314/0315 for interrupt vectors, but the routine should work with | ||
+ | 0314/315, but delay timing will be different becouse of the time being | ||
+ | up by the kernal> | ||
+ | |||
+ | < | ||
+ | d012 & $ d011 in the usual way> | ||
+ | |||
+ | <other initiation of program> | ||
+ | ... | ||
+ | |||
+ | jmp mainPrg | ||
+ | |||
+ | ; | ||
+ | |||
+ | sharp inc $d012 ;Set interrupt to the next line. | ||
+ | inc $d019 ; | ||
+ | ;same as lda #1 : sta $d019 | ||
+ | sta storeA | ||
+ | lda #< | ||
+ | sta $fffe ; | ||
+ | lda #> | ||
+ | sta $ffff | ||
+ | cli ; | ||
+ | ;still are in the previos irq call | ||
+ | noploop | ||
+ | nop | ||
+ | nop ; We add nop's here so that the next interrupt | ||
+ | ; will interrupt while we are executing nop | ||
+ | ... ; commands. Since nop commands use two cycles | ||
+ | ... ; the interrupt will at most be delayed 1 | ||
+ | ... ; cycle. | ||
+ | ... ; I think we needed about 11 nop's to be sure | ||
+ | ; that execution is interrupted while they are | ||
+ | ; run. (I'm not sure about this number) | ||
+ | |||
+ | jmp noploop | ||
+ | ; interrupt before we reach this point, we | ||
+ | ; add this loop to be on the safe side. | ||
+ | ; Imagine if the program happened to be frosen | ||
+ | ; by a carterigde and later restarted while | ||
+ | ; executing the nop's. | ||
+ | |||
+ | |||
+ | sharpirq | ||
+ | pla ; interrupt, we don't intend to return from | ||
+ | pla ; it. | ||
+ | stx storeX | ||
+ | sty storeY | ||
+ | |||
+ | nop ; Now we only have an uncertainty of 1 cycle | ||
+ | nop ; as to where the interrupt is. | ||
+ | ; By waiting until the edge of the current | ||
+ | ; rasterline we can determine if the interrupt | ||
+ | ... ; was delayed by one cycle or not. | ||
+ | ... ; (How many nop's that is required to reach the | ||
+ | ... ; edge of the rasterline i don't remember. | ||
+ | ; You'll just have to find it out yourself) | ||
+ | ; These nop's may of course be exchanged by | ||
+ | ; equivelent time consuming instructions. | ||
+ | |||
+ | lda $d012 ; get current rasterline | ||
+ | cmp $d012 ; still on same line = was delayed; | ||
+ | bne addCycle | ||
+ | addCycle | ||
+ | ; not doing one. | ||
+ | |||
+ | ; the rastertiming is now ' | ||
+ | rts ; return to routine that cal led sharp | ||
+ | |||
+ | |||
+ | endIrq | ||
+ | storeA | ||
+ | lda #0 ; For those who don't like self modifying code | ||
+ | storeX | ||
+ | ldx #0 | ||
+ | storeY | ||
+ | ldy #0 | ||
+ | rti ; Return from interrupt. | ||
+ | |||
+ | nextIrq | ||
+ | stx $fffe | ||
+ | sty $ffff | ||
+ | sta $d012 | ||
+ | inc $d019 ;or lda #1 : sta $d019 if you prefere | ||
+ | rts | ||
+ | |||
+ | ; | ||
+ | |||
+ | irq1 | ||
+ | jsr sharp ;Make interrupt sharp and store A X Y regs. | ||
+ | |||
+ | <here you put your sharp interrupt dependant code> | ||
+ | |||
+ | <other code that you need executed this interrupt> | ||
+ | |||
+ | ldx #< | ||
+ | ldy #> | ||
+ | lda #< | ||
+ | jsr nextIrq | ||
+ | |||
+ | jmp endIrq | ||
+ | |||
+ | ; | ||
+ | |||
+ | mainPrg | ||
+ | cli ; | ||
+ | |||
+ | <wait for space or something> | ||
+ | |||
+ | sei | ||
+ | |||
+ | <shut everything down and restore what needs to be restored> | ||
+ | |||
+ | rts ; Or jmp exitCode | ||
+ | |||
+ | ******************************************************************* | ||
+ | |||
+ | By adding this routine: | ||
+ | |||
+ | saver sta storeA | ||
+ | stx storeX | ||
+ | sty storeY | ||
+ | rts | ||
+ | |||
+ | ...you can at any time turn on/off the sharping by exchanging 'jsr sharp' | ||
+ | and 'jsr saver' | ||
+ | |||
+ | I hope this is what you were looking for. Sorry for not supplying a | ||
+ | complete source, but as I said this is all from memory. | ||
+ | |||
+ | PS. My native lanuage is not english and I typed this in a hurry, so sorry | ||
+ | for the lousy lanuage / source. | ||
+ | |||
+ | Furhermore, ways of getting an rnd. | ||
+ | |||
+ | * lda $d012, only in large complex programs with lots of variable execution | ||
+ | times where rnd is only needed once in a while. | ||
+ | | ||
+ | |||
+ | * lda $d800 ; The 4 upper bits of mem at area $d800 - $dc00 are completly | ||
+ | lsr ; unpredictable... | ||
+ | lda $d801 | ||
+ | lsr | ||
+ | lda $d802 | ||
+ | ... | ||
+ | |||
+ | * Read the values from the white noise generator to the SID. | ||
+ | 3rd voice set to whitenoise, and read the result. | ||
+ | (sorry don't remember address to read, think it is around $d416-$d41c) | ||
+ | |||
+ | These give you _true_ random numbers. It's also possible to create | ||
+ | ' | ||
+ | |||
+ | Anything unclear? Mail me at s514@ii.uib.no | ||
+ | |||
+ | Bye... | ||
+ | |||
+ | - Rolf Wilhelm Rasmussen | ||
+ | |||
+ | Equal of Eternity | ||
+ | |||
+ | =========================================================================== | ||
+ | </ | ||
+ | ====== C.S. Bruce Interview ====== | ||
+ | < | ||
+ | by Craig Taylor (duck@pembvax1.pembroke.edu). | ||
+ | |||
+ | The following is an interview of Craig Bruce, author of numerous programs such | ||
+ | as ZED, ACE etc for the Commodore 64/128. | ||
+ | |||
+ | > What computer experience did you have before the Commodore computers? | ||
+ | |||
+ | Very little. | ||
+ | My Jr. High school had a few CBM 8032s, but I never actually got to touch | ||
+ | one. I took a "mini course" | ||
+ | what computers were like and about BASIC. | ||
+ | I learned, I was entirely incapable at the time of figuring out how to | ||
+ | increment a variable (X=X+1). | ||
+ | |||
+ | > What was your first Commodore computer and why? | ||
+ | |||
+ | My first computer was a VIC-20. | ||
+ | TI99-4A, or a Timex/ | ||
+ | Ataris or the Apple). | ||
+ | computers at school but mostly because it had the most impressive brochure | ||
+ | and I had the most information about it. It was theoretically a Christmas | ||
+ | present, but it didn't stay in the box very long. I paid half of the $400.00 | ||
+ | price tag and my parents paid the other half. | ||
+ | |||
+ | > How did you learn programming on the Commodore? Did experience from other | ||
+ | PC's help? | ||
+ | |||
+ | Ahhh, those were the days. I learned programming from a few sources. | ||
+ | user's guide that came with the VIC was quite helpful, and I read the | ||
+ | magazines of the day, mostly Compute!s. | ||
+ | school and used the computers there who knew a thing or two, and two other | ||
+ | friends who got VIC-20s soon after me, so we learned from each other. | ||
+ | |||
+ | I also took a relatively informal night course that was offered for | ||
+ | programming VIC-20s. | ||
+ | just about everything that the course tought: BASIC. | ||
+ | class, the instuctor talked just a little bit about machine langauge, just | ||
+ | enough for me to understand what was in the VIC-20 Programmer' | ||
+ | Guide. | ||
+ | |||
+ | Experience from other PCs didn't really factor into things, since I had no | ||
+ | experience with any other PC. However, when the time came to learn about | ||
+ | other computers and other programming languages (in high school and bachelor | ||
+ | university), | ||
+ | understood so thoroughly how computers worked because of my VIC-20 | ||
+ | experience. | ||
+ | |||
+ | > What other interests, besides hacking on the Commodore, do you have? | ||
+ | |||
+ | Not many. I don't get out much and I have only a handful of friends. | ||
+ | spend most of my time sleeping, watching TV, net surfing, doing school work, | ||
+ | and/or hacking on Commodores. | ||
+ | I'm doing these other things, I'm usually thinking about hacking on | ||
+ | Commodores. | ||
+ | like biking, though. | ||
+ | |||
+ | > What is your feelings on the demise of Commodore? | ||
+ | |||
+ | Losing Commodore was a little sad, but as someone said on the newsgroup when | ||
+ | Commodore went under, "What has Commodore done for you lately?" | ||
+ | 8-bitters lost all support from Commodore long before its demise. | ||
+ | certainly don't blame them; 8-bit computers are a thing of the past and there | ||
+ | wasn't a big enough market to support a company the size of Commodore. | ||
+ | |||
+ | > Do you see anything in the future that signals anything that will extend the | ||
+ | useful lifetime of the Commodore 8-bits? | ||
+ | |||
+ | IMHO, there are only two things that 8-bit computers need to survive in the | ||
+ | hands of hobbiests indefinitely: | ||
+ | peripherals. | ||
+ | and Creative Micro Designs has been providing modern peripherals for us to | ||
+ | use. New application programs are needed too, but I'm assuming that | ||
+ | hobbiests + serious_system_software --> serious_new_application_programs. | ||
+ | (Perhaps this is a bit self-serving). | ||
+ | |||
+ | Eight-bit computers have two big advantages over bigger PCs: a much lower | ||
+ | price and a much higher understandability quotient. | ||
+ | important to hobbiests. | ||
+ | |||
+ | > Do you feel that with the addition of newer periphials that are gradually | ||
+ | superceeding the CPU's job (REU, RamLink, SwiftLink etc...) that the | ||
+ | Commodore 8-bit standard machine is no longer standard?? | ||
+ | |||
+ | Indeed, there are lots of options. | ||
+ | The original Commodores are quite limited, and this modern hardware is needed | ||
+ | to allow the Commodores to remain useful in the networked world. | ||
+ | important feature of all of these new products is that you can flip a switch | ||
+ | or pull out a cartidge and you're back to your little old standard Commodore. | ||
+ | Of course, who really wants to do this. | ||
+ | |||
+ | The reason that I have stayed with my little Commodore for all of these years | ||
+ | is that I believe that it still has quite enough power (using modern | ||
+ | peripherals and expanded memory) to do what I require of it. For example, | ||
+ | it's quite possible to have a nice little 17K text editor that can edit huge | ||
+ | files and be very useful. | ||
+ | kinds of snazzy features that requires a monsterous machine to run on to do | ||
+ | this. These multi-megabyte programs are simply bloated and poorly designed. | ||
+ | |||
+ | > Will there ever be an update to Zed? (a question asked on a lot of the | ||
+ | commericial providers) | ||
+ | |||
+ | Yes. I've been promising this for a long time, but the right time to do this | ||
+ | is finally near. | ||
+ | |||
+ | > What is the process that you use for writing your programs? | ||
+ | |||
+ | I'll start this answer with a Unix-fortune quotation: | ||
+ | |||
+ | "Real programmers don't draw flowcharts. | ||
+ | | ||
+ | much good it did them." | ||
+ | |||
+ | For complicated algorithms, I'll sit down write some pseudo-code, | ||
+ | always plan and write out complicated data structures. | ||
+ | I usually just sit down and write code, after kicking ideas around in my head | ||
+ | long enough for me to know what I have to do. | ||
+ | |||
+ | > It's been noticed that you have a " | ||
+ | Can you elaborate on this? | ||
+ | |||
+ | Guilty as charged. | ||
+ | mega-machine to run on fast enough. | ||
+ | and I have an axe to grind that little 8-bit Commodore computers offer quite | ||
+ | enough computing power for most applications that most people would use them | ||
+ | for. The exceptions are number-crunching, | ||
+ | computation. | ||
+ | quite enough. | ||
+ | effort is sometimes misspent (like in the printing to the screen in ACE -- I | ||
+ | still have a few more tricks up my sleeve though...), but I like to go a | ||
+ | little too far sometimes to make people go, "Wow! I didn't know this little | ||
+ | machine could do that so fast!!" | ||
+ | huge machine to get adequate performance. | ||
+ | is true, since programmers assume that they can be extra sloppy when | ||
+ | programming for huge machines). | ||
+ | |||
+ | As a user, I like crisp responsiveness. | ||
+ | computers that can sometimes be absent on big multi-user virtual-memory | ||
+ | machines. | ||
+ | |||
+ | I also have a big thing against backwards compatibility (" | ||
+ | raisins" | ||
+ | reason that ACE, for example, was designed from scratch rather then with the | ||
+ | pre-set limitation that all BASIC-compatible programs should run with it (a | ||
+ | la CS-DOS). | ||
+ | |||
+ | > Is there anything that you find particularly useful / handy about the | ||
+ | Commodore' | ||
+ | |||
+ | Yeah, it's simple. | ||
+ | |||
+ | > And the corallary: Is there anything particularly annoying? | ||
+ | |||
+ | Yeah, it's limited. | ||
+ | |||
+ | > What is currently in the works / planned?? | ||
+ | |||
+ | My Commodore job queue looks like the following: | ||
+ | |||
+ | 1. Update my Unix VBM file filter. | ||
+ | with run-length encoding compression. | ||
+ | |||
+ | 2. Work on ACE release #13: Internal cleanup. | ||
+ | | ||
+ | | ||
+ | | ||
+ | |||
+ | 3. Develop a new portable archiver format and write a C program for Unix, | ||
+ | " | ||
+ | |||
+ | 4. Write my next article for C= Hacking, which will be about the detailed | ||
+ | | ||
+ | the C128. This article will also include a minimal multitasking | ||
+ | | ||
+ | |||
+ | 5. Work on ACE release #14: Port Zed to ACE. Get the basic editing features | ||
+ | | ||
+ | |||
+ | 6. Work on ACE release #15: Finish the ACE assembler. | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | |||
+ | 7. Work on ACE release #16: Archiving. | ||
+ | | ||
+ | " | ||
+ | Look into " | ||
+ | |||
+ | 8. Start on BOS, the distributed multitasking microkernel operating system | ||
+ | for the 128. | ||
+ | |||
+ | (Actually, some of these things will be done by the time that C= Hacking | ||
+ | comes out). | ||
+ | |||
+ | Keep on Hackin' | ||
+ | |||
+ | =========================================================================== | ||
+ | </ | ||
+ | ====== Aligning 1541 Drives ====== | ||
+ | < | ||
+ | by Ward Shrake (taken from comp.sys.cbm) | ||
+ | |||
+ | A discussion regarding Commodore 1541 disk drive alignment procedures, with | ||
+ | suggestions. | ||
+ | |||
+ | Background information. | ||
+ | |||
+ | The best way I've ever seen to consistently and reliably get a 1541 disk | ||
+ | drive aligned perfectly, was caused by copy protection. It is sort of appropo | ||
+ | that copy protection, which usually causes the "head knock" problem that puts | ||
+ | drives out of alignment in the first place, should also be able to solve the | ||
+ | problem it created. | ||
+ | |||
+ | An older version of a disk utility program, (" | ||
+ | it), had copy protection that would not let you load the disk up unless your | ||
+ | disk alignment was perfect. While initially loading itself, it would search | ||
+ | and search, never quitting, until it found what it was looking for, exactly | ||
+ | where it was looking for it. It would stay in an endless loop, searching | ||
+ | forever, never making it to so far as the first screen. This essentially | ||
+ | " | ||
+ | illegal copy. | ||
+ | |||
+ | This quickly became the most hassle-free, | ||
+ | ever seen. I have seen and used most of the others; this method beat them | ||
+ | all, no contest, in my opinion. | ||
+ | |||
+ | The other programs, the ones made for aligning your drive, | ||
+ | consistently worked acceptably well, in my experience. Other technical users | ||
+ | apparently feel the same way about them, as the " | ||
+ | Commodores points out. They would work OK part of the time, or on part of the | ||
+ | drives you tried, but not all, I found. Or they would say you now had a | ||
+ | perfectly-aligned drive, but some difficult copy protection schemes would | ||
+ | still not load and run on the newly tuned-up drive friend. A friend of mine, | ||
+ | now deceased, once had a drive no alignment program could fix. We tried | ||
+ | everything we could find. After aligning it with a given method or program, | ||
+ | some programs would load that would not load before, but others would now no | ||
+ | longer work, that used to work before. All in all, it was very frustrating, | ||
+ | and the general feeling was that there has to be a better, easier, more | ||
+ | reliable way to do this. | ||
+ | |||
+ | All an alignment program has to do, is to make sure that when the disk drive | ||
+ | says it is precisely at a given track' | ||
+ | there, centered on that track. | ||
+ | |||
+ | There are other Commodore adjustments, | ||
+ | most common problem. Disk drive rotational speed can be adjusted, but it | ||
+ | usually is not the problem. In fact, I've seen more than one drive, that when | ||
+ | adjusted to read a program-reported " | ||
+ | quit reading disks; requiring speed to be set at a reported 310 rpm, to work | ||
+ | again. The end stop gap can also be adjusted, but I've never seen it be the | ||
+ | real culprit with a non-working disk drive. Your experience may vary, of | ||
+ | course, but I've always found that it is best to concentrate on alignment | ||
+ | first, then fool around with the other adjustments ONLY after alignment is | ||
+ | truly corrected, and only if it still refuses to work properly. | ||
+ | |||
+ | Once alignment is corrected, there are methods available to insure that it | ||
+ | stays that way. For instance, you can have the stepper motor' | ||
+ | mechanically pinned to its shaft, instead of merely relying on the factory' | ||
+ | interference fit to hold it. Commodore 1541 drives were made to be self- | ||
+ | aligning, apparently, which would be fine if "head knocking" | ||
+ | schemes were not around. Since they are, the pulley should, ideally, not be | ||
+ | allowed to turn on its shaft, which is what causes misalignment problems. | ||
+ | |||
+ | How I used to align 1541 disk drives.... | ||
+ | |||
+ | To precisely align a given 1541 disk drive, I used the old, unbroken copy I | ||
+ | had of Disector (v3.0, I think), and followed these steps. | ||
+ | drive off an disconnected, | ||
+ | the outer plastic casing of the drive. This exposed the electronics inside. | ||
+ | You then found and loosened (but not removed!) the two stepper motor mounting | ||
+ | screws, which are on the underside of the disk drive' | ||
+ | After that, you hooked the power cable back up, and hooked the drive to the | ||
+ | computer like it normally is. | ||
+ | |||
+ | Once you've done this, you set the drive up on one side, so that you can | ||
+ | (carefully!) reach into the mechanism, to physically rotate the stepper | ||
+ | motor, which would normally be on the bottom of the drive. You type in the | ||
+ | program' | ||
+ | screen went black (copy protection searching for certain info on the | ||
+ | diskette). This is where the program | ||
+ | |||
+ | Once the program is loading, but stuck and unable to find what it wants, you | ||
+ | reach into the mechanism, very slowly and carefully, turning the stepper | ||
+ | motor a slight bit in either direction, and stopping. Tiny adjustments are a | ||
+ | lot; don't overdo it. Be patient; don't go too fast, or move it too much! You | ||
+ | watch the screen carefully, and listen to the drive' | ||
+ | |||
+ | When you have rotated the stepper motor to the proper place, the sounds and | ||
+ | the screen will act a little different, perhaps only slightly so. Wait a | ||
+ | second, not moving the stepper motor at all. When you are right on, | ||
+ | alignment-wise, | ||
+ | program' | ||
+ | |||
+ | Once the main menu has come onto the screen, you have a perfectly aligned | ||
+ | drive. Then you have to retighten the stepper mounting screws, being very | ||
+ | careful not to accidentally move the motor in the process. Hold the motor | ||
+ | firmly while retightening both screws in small steps, alternating back and | ||
+ | forth between them until they are both tight. The rotational force of the | ||
+ | screws turning, forces the motor to move some, so watch for it. | ||
+ | |||
+ | With this method, using a specially-prepared disk, I always got perfect | ||
+ | results; everything would load, every time, from then on. (Assuming that the | ||
+ | disk was formatted with a good drive to begin with; any disks you made | ||
+ | recently, on your badly-aligned drive, may not load after the alignment | ||
+ | procedure. | ||
+ | before you do this procedure. This is normal, however, no matter what method | ||
+ | you use to align a bad drive.) | ||
+ | |||
+ | Here's the problem with this method... | ||
+ | |||
+ | This procedure only works with a special disk, one that is no longer | ||
+ | available. With the special disk, alignment is quick, hassle-free, | ||
+ | always gave excellent, reliable results the first time around. Without the | ||
+ | " | ||
+ | since the method relies on a disk that is no longer available to the public. | ||
+ | You can't make your own, because you don't know if the disk drives you are | ||
+ | using, are truly perfect to start with! Disks made by users, on Commodore | ||
+ | equipment, never worked; they just matched your drive' | ||
+ | someone else's equipment, which may be borderline bad to start with. | ||
+ | |||
+ | Here's what I suggest to solve this... | ||
+ | |||
+ | Your mission, should some hot programmer out there choose to accept it, is to | ||
+ | create a program that will create a " | ||
+ | Commodore-compatible program to try to read that special disk. | ||
+ | |||
+ | Ideally, the Commodore-compatible reading program would be short and simple | ||
+ | enough to fit inside 8k of memory, so it could fit on a cartridge. This would | ||
+ | allow it to work, even if a user's disk drive would not load programs | ||
+ | anymore. It could still be stored on a diskette, too, with a little planning. | ||
+ | |||
+ | Theoretically, | ||
+ | on cartridge, you would only need a screwdriver to take the drive apart, and | ||
+ | a Commodore microcomputer to run the program on. No other special tools would | ||
+ | be needed, and very little technical knowledge would be required; just some | ||
+ | general safety tips, because you are working around sensitive electronic | ||
+ | parts, with wall current coming into the drive itself, at least on older | ||
+ | 1541' | ||
+ | |||
+ | Why should a programmer go to all the trouble? | ||
+ | |||
+ | I'm sure there are a lot of people out there who could use this, if some hot | ||
+ | programmer should decide to write it, and make it available to the rest of | ||
+ | us. There are always programmers out there, somewhere, eager to show off | ||
+ | their computer skills, and their creativity. It is one of the things that | ||
+ | makes the computer community so great in the first place. (If people out | ||
+ | there can write IBM-to-Commodore disk file readers, this should be a breeze!) | ||
+ | |||
+ | Techies should appreciate it as a great, reliable and cheap way to align | ||
+ | troublesome disk drives, and those people with a C64 in a closet would sure | ||
+ | appreciate their technical buddies getting their dead systems going again! | ||
+ | |||
+ | Description of what I have in mind, as to how it should work. | ||
+ | |||
+ | You need a Commodore computer (the 64 is most popular), one 1541 disk drive | ||
+ | that needs alignment (or that you want to check), a screwdriver to open the | ||
+ | drive up, a specially-prepared disk used only for alignment purposes, and a | ||
+ | computer program that would run on the Commodore that would look at and | ||
+ | analyze the information that is on the specially-prepared diskette, as the | ||
+ | computer program tries to read it. | ||
+ | |||
+ | OK. Here's where it gets cute. | ||
+ | |||
+ | The problem with most disk alignment methods, as I see it, is that it relies | ||
+ | totally on technology that the Commodore has available; trying to create a | ||
+ | special disk on a 1541, I just don't see as being realistic, or the best way | ||
+ | to do it. The 1541 has many limitations, | ||
+ | which operate on other computer platforms. Don't get me wrong; I love | ||
+ | Commodore computers, and have for years. But, realistically, | ||
+ | drives found in say an IBM machine, are just plain better in many ways than | ||
+ | the 1541. They are made to hold much more information, | ||
+ | have to be much more precise in doing so than the 1541 was ever designed to | ||
+ | be. | ||
+ | |||
+ | If a person were to do this, I would suggest that they write an IBM program | ||
+ | that would use a high density, 1.2 megabyte capacity, 5.25-inch type of disk | ||
+ | drive to create the special diskettes, which the 1541 would later read. | ||
+ | |||
+ | Doing this would allow the creation of very thin tracks on the diskette' | ||
+ | surface, spaced closely together. This would, within the limitations of the | ||
+ | 1541's read head, allow the Commodore to " | ||
+ | was, to one side or the other of some " | ||
+ | thin tracks, widthwise, is that the read head won't see them at all, | ||
+ | reliably, unless you are exactly, perfectly right on top of them. Another | ||
+ | advantage to this, again within the limitations of the 1541's read head | ||
+ | (whatever that may be), is that left or right of center, the head would | ||
+ | likely pick up the next track over, letting you know you were off by a | ||
+ | certain amount automatically. | ||
+ | |||
+ | I hope I'm making myself clear, in my explanation of this. If I am not, Email | ||
+ | me with your questions, and I'll try to answer them better, and/or update | ||
+ | this file, to entice someone else to work on this. I really would like to see | ||
+ | it done. (Current Email address, as of Sep 94: wardshrake@aol.com on the | ||
+ | Internet, or just WardShrake on AOL. Will soon have a Compuserve Email | ||
+ | address, too: I'll be user 75207,1005 there, or 75207.1005@compuserve.com on | ||
+ | the Internet.) | ||
+ | |||
+ | Anyway, let's continue. With the IBM creating a specially-made disk just for | ||
+ | this one purpose, you would not even have to worry about following any | ||
+ | standard formatting procedures. No user-stored data would ever be written to | ||
+ | the diskette, so standard sectoring could be safely ignored. You could create | ||
+ | any signal or sectoring scheme you like, as long as the IBM could create it, | ||
+ | and the Commodore could read it; and you'd be writing both programs, anyway, | ||
+ | making this easier to insure, right? | ||
+ | |||
+ | I can hear some die-hard Commodore users saying, "I hate IBM' | ||
+ | even have an IBM" or some such. Fine. Not a problem. If all the | ||
+ | IBM-compatible program did was to create a special floppy disk, once, then | ||
+ | quit, you would not even need to OWN an IBM, you'd just need to be able to | ||
+ | USE one for a few minutes. | ||
+ | |||
+ | Even if you don't have access to one at work, and don't know of anyone who | ||
+ | has one to lend you, I will stick with this suggestion, because I know that | ||
+ | some businesses that make photocopies often also rent IBM's and Mac's on an | ||
+ | hourly basis, for very little money. My local Kinko' | ||
+ | both at $10.00 an hour. You would only need it for a few minutes or so. | ||
+ | |||
+ | The diskette-creation program would only need a few minutes to run, to make | ||
+ | up a special disk, so you'd only be paying for a good quality, blank high | ||
+ | density floppy, and ten or fifteen minutes of rental time, tops. The copy | ||
+ | center person may even be able to start up the floppy-based IBM program for | ||
+ | you, if you don't know how to do it yourself. That should come to $5.00 or | ||
+ | less, even if you don't own or normally have access to an IBM compatible | ||
+ | computer! You can't beat that, for a utility to align equipment! | ||
+ | |||
+ | OK. In overview, you'd need to use an IBM-compatible computer, just long | ||
+ | enough to load an IBM-compatible program which would create one special, | ||
+ | 5.25" diskette, perhaps on a high density floppy. You would then open up your | ||
+ | Commodore drive' | ||
+ | to read the created diskette. (Again, an 8k Commodore program would fit very | ||
+ | easily on a cartridge, for easiest loading and running.) | ||
+ | |||
+ | While the computer and drive were running, you would (very carefully, and | ||
+ | observing safety precautions) loosen the stepper motor' | ||
+ | turn the motor clockwise and counter-clockwise, | ||
+ | screen info told you that you were exactly where you should be, right over | ||
+ | the proper track. Not to the left or right of it, but in perfect alignment. | ||
+ | |||
+ | Because the Commodore disk-reading program would be " | ||
+ | reporting any small changes to you via information on your screen, you would | ||
+ | only have to take a few minutes of fiddling, doing a simple, non-technical | ||
+ | turning of the stepper motor, to get the drive aligned. The two computer | ||
+ | programs that would make up this package would be doing most of the work. | ||
+ | |||
+ | I imagine a drive could be perfectly aligned, and back in running order, in | ||
+ | fifteen minutes or less. Five, if you paid attention to the process, and had | ||
+ | some practice before. Remember, this is based on an alignment procedure I | ||
+ | really used to do, using a heavily-protected diskette, so I am extrapolating | ||
+ | from my personal experiences, | ||
+ | |||
+ | I don't see where there would be any easier, simpler method of doing a disk | ||
+ | alignment. The user wouldn' | ||
+ | sectors; they would just loosen two screws, following some instructions, | ||
+ | turn the motor. What could be any easier? | ||
+ | |||
+ | The program could, if it was really creative and well-done, tell them to | ||
+ | rotate the motor clockwise or counter-clockwise (as they face it), to dial | ||
+ | the motor precisely in. Tracks to either side of an arbitrary (track 18?) | ||
+ | center position would say to go one way, tracks on the other would say the | ||
+ | reverse. When you turn it too far one way, it would reverse its instructions | ||
+ | to you; you would know you were very close then. When you were "right on," | ||
+ | the program would tell you so. You'd lock the screws down, carefully, and as | ||
+ | long as you hadn't jiggled the motor when you tightened it back down, you | ||
+ | would be all done! | ||
+ | |||
+ | How much easier could it be, right? (On the final user, that is!) | ||
+ | |||
+ | If anyone is interested in doing this, or goes out and does it, please let me | ||
+ | know via Email. I'd like to hear about it. Again, it would be something | ||
+ | possible, useful, and a really neat trick. I know there are people out there | ||
+ | that program on both the IBM and the Commodore; the various cross-reading | ||
+ | programs attest to that, well enough! | ||
+ | |||
+ | Ward Shrake | ||
+ | Covina, California | ||
+ | |||
+ | ==================================================================---END---=== | ||
+ | </ |
magazines/chacking9.txt · Last modified: 2015-04-17 04:34 by 127.0.0.1