magazines:chacking8
no way to compare when less than two revisions
Differences
This shows you the differences between two versions of the page.
— | magazines:chacking8 [2015-04-17 04:34] (current) – created - external edit 127.0.0.1 | ||
---|---|---|---|
Line 1: | Line 1: | ||
+ | < | ||
+ | ######## | ||
+ | ################## | ||
+ | ###### | ||
+ | ##### | ||
+ | ##### #### #### ## ##### #### | ||
+ | ##### ## ## #### ## ## | ||
+ | ##### | ||
+ | ##### ## ## ######## | ||
+ | ##### #### #### #### #### ##### #### | ||
+ | ##### ## | ||
+ | ###### | ||
+ | ################## | ||
+ | ######## | ||
+ | ------------------------------------------------------------------------------ | ||
+ | </ | ||
+ | ====== Editor' | ||
+ | < | ||
+ | by Craig Taylor (duck@pembvax1.pembroke.edu) | ||
+ | |||
+ | Woe be to Commodore, | ||
+ | The marketer' | ||
+ | With a little bit of spending here, | ||
+ | And not much over there, | ||
+ | You know that Commodore has finally died. | ||
+ | | ||
+ | And that's the way life should be, | ||
+ | The Commodore fanatics cried. | ||
+ | | ||
+ | Let Commodore go to hell. | ||
+ | So the question is who'll purchase Commodore? | ||
+ | |||
+ | Yes, for those of you who are unaware Commodore has declared bankruptcy. | ||
+ | There are numerous rumours abound over whose interested in what divisions of | ||
+ | Commodore and such - there' | ||
+ | factors can be blamed for Commodore' | ||
+ | successful in marketing products. The engineers would often turn out miracle | ||
+ | machines that the marketing department (I wonder if there even was one) | ||
+ | promoted badly, if at all. What has put the nail in the coffin for Commodore | ||
+ | was the lack of financial capital to keep the company in operation. A lot of | ||
+ | this news has been discussed on GEnie, the newsgroup Comp.Sys.Cbm, | ||
+ | various magazines as well so I won't elaborate any further. | ||
+ | |||
+ | Speaking of magazines, Creative Micro Designs has started a magazine for | ||
+ | Commodore 8-bit' | ||
+ | There are other magazines that also deserve mention: DieHard, the Underground | ||
+ | and many others. Commodore may be bankrupt but the Commodore will still live | ||
+ | forever. | ||
+ | |||
+ | Speaking of living forever, Commodore Hacking is looking for articles on any | ||
+ | subject dealing with any aspect of technical programming or hardware on the | ||
+ | Commodore. If you've got an article already written, or an idea for one | ||
+ | _please_ feel free to e-mail me via duck@pembvax1.pembroke.edu. Many thanks | ||
+ | to the authors whose works make up this and previous issues. | ||
+ | |||
+ | ============================================================================= | ||
+ | |||
+ | Please note that this issue and prior ones are available via anonymous | ||
+ | FTP from ccnga.uwaterloo.ca (among others) under pub/ | ||
+ | and via a mailserver which documentation can be obtained by sending | ||
+ | mail to " | ||
+ | " | ||
+ | message. | ||
+ | |||
+ | ============================================================================= | ||
+ | |||
+ | NOTICE: Permission is granted to re-distribute this " | ||
+ | whole, 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 / diskette costs for this " | ||
+ | |||
+ | ============================================================================= | ||
+ | </ | ||
+ | ====== In This Issue ====== | ||
+ | < | ||
+ | |||
+ | Commodore Trivia Corner | ||
+ | |||
+ | This section of C=Hacking contains questions that test your knwoledge of | ||
+ | tricks and little known-information for the Commodore computers. Each issue | ||
+ | they' | ||
+ | much do you know? | ||
+ | |||
+ | RS232 Converter | ||
+ | |||
+ | This article, with a minimum of parts, details how to make your own RS-232 | ||
+ | converter. | ||
+ | |||
+ | Programming the Commodore RAM Expansion Units (REUs) | ||
+ | |||
+ | The REC chip is a DMA (direct memory access) chip that allows for the | ||
+ | Commodore 64 and 128 to use the Ram Expansion Units. This article examines | ||
+ | how to access the chip in your own ML programs. | ||
+ | |||
+ | A Different Perspective: | ||
+ | |||
+ | In this article, co-written by Stephen Judd and George Taylor, is | ||
+ | presented all the basic graphics tools and mathematical theory behind | ||
+ | 3d graphics. | ||
+ | plotting a point, drawing a line, clearing the graphics, and double | ||
+ | buffering. | ||
+ | object in 3d space, and perspective viewing on a 2d screen. | ||
+ | Programs are presented in basic 2.0, basic 7.0, and assembly which | ||
+ | show a rotating cube outline. | ||
+ | |||
+ | Design of a ' | ||
+ | |||
+ | Written Craig Bruce this article examines a ' | ||
+ | Commdore 128. It focuses on the OS being Multi-tasking, | ||
+ | on a MicroKernal. | ||
+ | what interests me. The ease-of-construction thing is important too. Another | ||
+ | important question is 'can it be done?' | ||
+ | be done, whenever I get around to it (one of these lifetimes)." | ||
+ | |||
+ | ============================================================================= | ||
+ | </ | ||
+ | ====== Commodore Trivia Corner ====== | ||
+ | < | ||
+ | by Jim Brain (brain@mail.msen.com) | ||
+ | |||
+ | It is time for another dose of trivia! | ||
+ | Commodore Trivia Editions are posted every month to the USENET newsgroups | ||
+ | comp.sys.cbm, | ||
+ | article is a compilation Trivia Editions 2-8, with only the questions | ||
+ | appearing for Edition 8. These are part of a Trivia Contest in which | ||
+ | anyone may enter. | ||
+ | Trivia contest (Which is on Trivia 8 as I write this), please send your | ||
+ | answers to me at ' | ||
+ | |||
+ | The following article contains the answers to the January edition of trivia | ||
+ | ($00A - $01F), the questions and answers for Febrary ($020 - $02F), March | ||
+ | ($030 - $03F), April ($040 - $04F), May ($050 - $05F), June ($060 - $06F), | ||
+ | and the questions for the July edition ($070 - $07F). | ||
+ | |||
+ | |||
+ | Here are the answers to the Commodore trivia questions for January, 1994. | ||
+ | |||
+ | |||
+ | Q $00A) What was the Code-Name of the Amiga while in Development? | ||
+ | |||
+ | A $00A) Lorraine. | ||
+ | company, they scrapped the model name and used the old company name. | ||
+ | |||
+ | Q $00B) What is Lord British' | ||
+ | Series)? | ||
+ | |||
+ | A $00B) Richard Garriott. | ||
+ | of astronaut Owen Garriott. | ||
+ | |||
+ | Q $00C) What is the POKE location and value that will fry an early model | ||
+ | PET? | ||
+ | |||
+ | A $00C) 59458. | ||
+ | No, I won't tell you what to poke into it, but I will tell you | ||
+ | that it is not the only way to fry a PET. here is a description from | ||
+ | none other than Jim Butterfield | ||
+ | |||
+ | "The poke shopwn above is correct. Its intention was to speed up early | ||
+ | model PETs by masking the RETRACE line (by switching it to output)... | ||
+ | |||
+ | however, Commodore subsequently REDESIGNED the interface in such a way | ||
+ | that making the VIA pin an output caused (now) two outputs to fight | ||
+ | each other ... result, VIA and/or video circuitry burnt out. | ||
+ | |||
+ | LATER (Days of "fat 40" and 80-column PETs), the new CRT controller | ||
+ | chip could be fiddled with POKES so that it generated scan rates | ||
+ | completely out of the capacity of the CRT deflection circuits. | ||
+ | Result: burnt out deflection circuitry ... and that was no YOKE!" | ||
+ | |||
+ | Richard Bradley says that 59595 is the second poke that Jim is | ||
+ | referring to. | ||
+ | |||
+ | I also have in on word from Ethan Dicks that 59409 is another | ||
+ | infamous poke, but I wouldn' | ||
+ | |||
+ | Q $00D) On the Plus 4 and C-16, the VIC chip was replaced with the TED | ||
+ | chip. | ||
+ | |||
+ | A $00D) TED = Text Editing Device. | ||
+ | as the VIC II. | ||
+ | |||
+ | Q $00E) Commodore Produced a daisy-wheel letter quality printer in North | ||
+ | America (maybe elsewhere) for the Commodore Serial Line. Name it. | ||
+ | |||
+ | A $00E) The Commodore DPS 1101. The CBM 6400 was another earlier attempt | ||
+ | at a daisy-wheel printer, but it had an IEEE-488 interface. | ||
+ | |||
+ | Q $00F) What is the version of DOS in the 1541? | ||
+ | |||
+ | A $00F) 2.6 | ||
+ | |||
+ | Q $010) What is the Version of BASIC in the Plus 4 and the C-16? | ||
+ | |||
+ | A $010) 3.5. | ||
+ | |||
+ | Q $011) What are the nicknames of the original three custom Amiga chips? | ||
+ | |||
+ | A $011) Daphne/ | ||
+ | Denise, Agnes, and Paula were the American names, but the the others | ||
+ | crept in from somwhere. | ||
+ | as alternate names. | ||
+ | |||
+ | Q $012) Commodore produced a 64 in a PET case. What is its name and model | ||
+ | number? | ||
+ | |||
+ | A $012) The Educator 64. It was model number CBM 4064, and it was also called | ||
+ | the PET64. | ||
+ | Commodore first tried to sell the " | ||
+ | regular 64 case, but administrators and teachers disliked the " | ||
+ | look. | ||
+ | I don't think it was ever a killer seller. | ||
+ | |||
+ | Q $013) Commodore sold a 1 megabyte floppy disk drive in a 1541 case. | ||
+ | Give the model number. | ||
+ | |||
+ | A $013) The Commodore SFD 1001. It was actually half of an CBM 8250 LP | ||
+ | with a slightly revised ROM. | ||
+ | |||
+ | Q $014) What does GCR stand for? | ||
+ | |||
+ | A $014) Group Code Recording. | ||
+ | |||
+ | Q $015) Commodore produced a drive to accompany the Plus 4 introduction that | ||
+ | was designed specifically for the Plus/ | ||
+ | | ||
+ | A $015) the CBM 1551 was the new, high-performance drive that was designed | ||
+ | specifically for the Commodore Plus/4 and C-16. The 1542 was | ||
+ | actually just a repackaged 1541 in a grey case that was made available | ||
+ | for people who didn't want to spend the extra money for the 1551. The | ||
+ | extra cost resulted from the 1551 sporting a new, parallel transfer | ||
+ | method that increased transfer rates 400%. | ||
+ | |||
+ | Q $016) What does SID stand for? | ||
+ | |||
+ | A $016) SID = Sound Interface Device | ||
+ | |||
+ | Q $017) What does the acronym KERNAL stand for? | ||
+ | |||
+ | A $017) KERNAL = Keyboard Entry Read, Network, And Link. This is most likely | ||
+ | another "words after the letters" | ||
+ | PET acronym. | ||
+ | |||
+ | Q $018) What version of DOS does the 1571 have? | ||
+ | |||
+ | A $018) 3.0 | ||
+ | |||
+ | Q $019) What other two Commdore Disk Drives share the same DOS version | ||
+ | number as the 1571? | ||
+ | |||
+ | A $019) I got more than I bargained for on this question, since there | ||
+ | are four drives which have the same DOS version that I feel are | ||
+ | | ||
+ | |||
+ | The CBM D9060 and D9090, although I doubt the code is the same. | ||
+ | The D series were hard drives. | ||
+ | |||
+ | The 8280 Dual 8" Floppy Drive. | ||
+ | |||
+ | The 1570, which was a single sided version of the 1571 in a 1541 | ||
+ | case painted to match the 128. The ROM is slightly different, | ||
+ | enough to make it unrecognizable as either a 1541 or a 1571 in some | ||
+ | cases. | ||
+ | |||
+ | The 1571II and the 1571D, which is the drive in the C128D, also | ||
+ | have this DOS revision, but that stands to reason, since they are | ||
+ | in the 1571 line. | ||
+ | |||
+ | Q $01A) How many files will the 1571 hold? | ||
+ | |||
+ | A $01A) 144 in both modes. | ||
+ | put another directory on the back. | ||
+ | |||
+ | Q $01B) How many files will the 1541 hold? | ||
+ | |||
+ | A $01B) 144. | ||
+ | |||
+ | Q $01C) What did Commodore make right before entering the computer market? | ||
+ | |||
+ | A $01C) Calculators. | ||
+ | machines, and thermostats, | ||
+ | Machines" | ||
+ | |||
+ | Q $01D) Commodore introduced an ill-fated 4 color plotter. | ||
+ | number. | ||
+ | |||
+ | A $01D) the Commodore 1520. It used 4 inch wide paper and could use 4 | ||
+ | colors. | ||
+ | |||
+ | Q $01E) Some formats of CP/M write disks using the MFM format. | ||
+ | MFM stand for? | ||
+ | |||
+ | A $01E) MFM = Modified Frequency Modulation | ||
+ | |||
+ | Q $01F) On the Commdore 128, the user manual left three commands undocumented. | ||
+ | One works, and the other gives a not-implemented error. | ||
+ | commands and what each one does or does not do. | ||
+ | |||
+ | A $01F) RREG reads the internal registers after a SYS command. | ||
+ | OFF gives an unimplemented command error. | ||
+ | QUIT does too. | ||
+ | |||
+ | |||
+ | Here are the answers to Commodore Trivia Edition #3 for February, 1994. | ||
+ | |||
+ | | ||
+ | Q $020) What does the letters IEEE in IEEE-488 stand for? | ||
+ | |||
+ | A $020) Institute of Electrical and Electronics Engineers. | ||
+ | |||
+ | Q $021) What was the logo of Batteries Included? | ||
+ | |||
+ | A $021) It was a the face and hands of a man with glasses inside a circle. | ||
+ | Early renditions of him were in black and white, while later ones had | ||
+ | him with blond hair a a red shirt. | ||
+ | typing on the 64/VIC with one finger, but most just showed him, | ||
+ | not the keyboard. | ||
+ | |||
+ | Q $022) The Commodore VIC-20, 64, and 128 computers emulate in software a very | ||
+ | important integrated circuit. What is its number, and why is it | ||
+ | important? | ||
+ | |||
+ | A $022) The 6551 UART IC. It is used for RS-232 communications. | ||
+ | |||
+ | Q $023) Commodore watches play a beautiful song for the alarm. | ||
+ | song's title? | ||
+ | |||
+ | A $023) Fleur-de-lis. | ||
+ | |||
+ | Q $024) The C2N style Commodore tape decks are impressive in handling errors. | ||
+ | How many times is a single program stored onto tape? | ||
+ | |||
+ | A $024) Twice, second copy is placed right after the first. | ||
+ | if you get a load error on load, you might be able to just run the | ||
+ | program anyway, as a load puts the first copy in memory, and verifies | ||
+ | it against the second copy. | ||
+ | | ||
+ | Q $025) What is a jiffy? | ||
+ | |||
+ | A $025) A jiffy is 1/60th of a second. | ||
+ | Commodore computers. | ||
+ | |||
+ | Q $026) What is the screen resolution of the Commodore VIC-20? | ||
+ | |||
+ | A $026) On the VIC-I IC, the text and graphics screens are definable within | ||
+ | limits. | ||
+ | |||
+ | The default screen has (and the answers I was looking for): | ||
+ | |||
+ | Text: 22H x 23V = 506 characters | ||
+ | Graphics: 176H x 184V = 32384 pixels | ||
+ | | ||
+ | However, on experimentation with a NTSC VIC-I (6560), I found that | ||
+ | it could support a resolution of: | ||
+ | |||
+ | Text: 24H x 29V = 696 characters | ||
+ | Graphics: 192H x 232V = 44544 pixels | ||
+ | |||
+ | Your mileage may vary, but these numbers remove all border area. | ||
+ | (I am not sure if you can use all the pixels, since the VIC-I only | ||
+ | allows 32768 to be used. You might be able to flip the graphics | ||
+ | page in the middle of the screen, but I leave that as an exercise.) | ||
+ | | ||
+ | The VIC-I also supports a virtual screen, which can be " | ||
+ | that the physical screen becomes a " | ||
+ | The maximum " | ||
+ | |||
+ | Text: 28H x 32V? = 896 characters | ||
+ | Graphics: 224H x 256V? = 57344 pixels | ||
+ | |||
+ | The VIC supports more resolution than 32V, but you can never see | ||
+ | it since you can't scroll it into view, so the point is moot. | ||
+ | |||
+ | So, if I didn't thoroughly confuse you, email me and I will make | ||
+ | sure I do! | ||
+ | |||
+ | Q $027) Why is the VIC-20 named the VC-20 in Germany? | ||
+ | |||
+ | A $027) Because ' | ||
+ | pronunciation was a naughty word. | ||
+ | |||
+ | Commodore put one over on many people. | ||
+ | the states and given that name due to the IC that did the graphics. | ||
+ | When the marketing started, CBM found out the name was no good in | ||
+ | Germany, so they quickly renamed it VC-20. | ||
+ | Volks-Computer conjured up images of the Volkswagon car (VW), which | ||
+ | was popular at the time for its dependability and price. | ||
+ | history... | ||
+ | |||
+ | Q $028) Why was early Commodore equipment built into such heavy enclosures? | ||
+ | |||
+ | A $028) Simple. | ||
+ | filing cabinets. | ||
+ | The fact that, at the time the PET came out, people equated physical | ||
+ | stability of a machine as an indication of its worth, served only to | ||
+ | reinforce the decision. | ||
+ | monitor. | ||
+ | |||
+ | Most people think it is due to FCC regulations. | ||
+ | not been determined at the time the PET came out, although the | ||
+ | engineers did know that the CRT produced many electrical hazards which | ||
+ | could be alleviated with a shielded metal case. Commodore has always | ||
+ | been a " | ||
+ | shielding in-house at almost no cost proved to be the overriding | ||
+ | factor. It might interest some to note that, even with the metal | ||
+ | case, early PETs had foil inside as a secondary shield. | ||
+ | has to do with the keyboard being mostly plastic, as the shield fit | ||
+ | directly underneath, but the reason for it remains a mystery to me. | ||
+ | | ||
+ | Q $029) What two BASIC 2.0 commands might still work if mispelled? | ||
+ | |||
+ | A $029) The answers I was looking for are END and STOP, although someone | ||
+ | correctly pointed out that GO TO can be construed as a mispelling. | ||
+ | Also, print#, get#, and input# might work if the '#' | ||
+ | the program was getting data to screen or keyboard. | ||
+ | |||
+ | Although the following aren't really the result of mispelled commands, | ||
+ | I put them in, since you could stretch the definition of mispelled to | ||
+ | include them. | ||
+ | |||
+ | LET would work if it was left out, since LET was an optional | ||
+ | keyword. | ||
+ | work if letters were tacked onto the end. (example: RUNDY., prg has | ||
+ | a valid line 0, and DY = 0). Finally, LOAD" | ||
+ | work due to the way LOAD absolute worked, but that is a stretch! | ||
+ | |||
+ | Q $02A) What does CIA stand for? (not the U.S. CIA!) | ||
+ | |||
+ | A $02A) CIA = Complex Interface Adapter. | ||
+ | it a Control Interface Adapter, but that is not its official | ||
+ | name. | ||
+ | |||
+ | Q $02B) (hard one) What is the key VIC capability that makes full-screen | ||
+ | hires graphics possible on the _VIC-20_? | ||
+ | |||
+ | A $02B) A lot of people answered redefinable characters, but that alone does | ||
+ | not provide FULL-SCREEN graphics. 256 8*8 cells gives you a little | ||
+ | over 1/2 of the screen in graphics, but the VIC has the ability to | ||
+ | make each character cell be 8*16, which gives enough pixels to map | ||
+ | the entire default screen. | ||
+ | |||
+ | Q $02C) How many cassette ports does the CBM 8032 computer have? | ||
+ | |||
+ | A $02C) Two. One on back, one on side near the back. | ||
+ | |||
+ | Q $02D) What 5 bytes must appear in every Commodore 64 autostart cartrdge and | ||
+ | what location in memory must they be placed at? | ||
+ | |||
+ | A $02D) CBM80 at $8004. | ||
+ | PETSCII codes are 195, 194, 205, 056, 048. | ||
+ | $c3, $c2, $cd, $30, $30 in HEX | ||
+ | |||
+ | Q $02E) What is the correct Commodore technical term for " | ||
+ | |||
+ | A $02E) MOBs, or Movable Object Blocks. | ||
+ | |||
+ | Q $02F) (Three parter, all parts must be correct) | ||
+ | nickname for a condition that can lock up an old-style C=64. | ||
+ | What causes it? | ||
+ | How can it be avoided (besides not doing it)? | ||
+ | What is the only way out once it has occured (besides rebooting)? | ||
+ | |||
+ | A $02F) Wow, I got so many responses to this! This question actually | ||
+ | dealt with a typical user, but people sent in descriptions of | ||
+ | what the code does and how to patch it. So, there are two sets | ||
+ | of answers to this: | ||
+ | |||
+ | User Answer: | ||
+ | |||
+ | 1) If you put the cursor at the bottom of the screen and type 82 characters | ||
+ | (not 81) and then trying to delete back to the 78th one. | ||
+ | 2) Any of the following will work: | ||
+ | | ||
+ | Do not use the following colors for the cursor: red, blue, yellow, | ||
+ | light red, dark grey, light blue, light gray. | ||
+ | | ||
+ | Some people devised a IRQ wedge that will recover from the lockup. | ||
+ | | ||
+ | Have the following lines as the first lines of a program: | ||
+ | 10 open 15,8,15 20 input# | ||
+ | 3) There are actually two ways to recover. | ||
+ | | ||
+ | If you have a reset button installed on the 64, reset the machine, | ||
+ | then load and run an unnew program. | ||
+ | most people would assume this much) | ||
+ | | ||
+ | If you have a tape drive installed, press either Shift-3 or move a | ||
+ | joystick installed in Port 1 in the UP direction. | ||
+ | the directions on the screen "PRESS PLAY ON TAPE". Next, press | ||
+ | RUN-STOP to stop the tape load. | ||
+ | | ||
+ | |||
+ | What really happens: (I can't prove this) | ||
+ | | ||
+ | 1) The user types the line of text and the scroll code is invoked. | ||
+ | The first two lines become linked as one logical line, and the | ||
+ | third line is treated as a new line. | ||
+ | | ||
+ | The user deletes the 82nd and the 81st character and then hits delete | ||
+ | while in the first column of the third line. Since the delete will put | ||
+ | the cursor back up into the second line, which is linked with the first, | ||
+ | the KERNAL gets confused and thinks the second line is at the bottom of | ||
+ | the screen. Remember, the " | ||
+ | | ||
+ | | ||
+ | from the first column of the last line, the KERNAL thinks the color | ||
+ | nyble for it is at $DC00, which is 40 bytes off from the actual | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | Now, the system integrity has been compromised, | ||
+ | | ||
+ | | ||
+ | fools the KERNAl into thinking SHIFT/ | ||
+ | | ||
+ | | ||
+ | 2) Since the Color RAM is what the KERNAl gets confused about, the solution | ||
+ | was to not use certain bit patterns of colors: | ||
+ | | ||
+ | RED 0010 | ||
+ | CYAN 0011 | ||
+ | BLUE 0110 | ||
+ | YELLOW | ||
+ | LIGHT RED 1010 | ||
+ | DARK GRAY 1011 | ||
+ | LIGHT BLUE 1110 | ||
+ | LIGT GRAY 1111 | ||
+ | | ||
+ | OK Colors: | ||
+ | | ||
+ | BLACK 0000 | ||
+ | WHITE 0001 | ||
+ | PURPLE | ||
+ | GREEN 0101 | ||
+ | ORANGE | ||
+ | BROWN 1001 | ||
+ | MEDIUM GRAY 1100 | ||
+ | LIGHT GREEN 1101 | ||
+ | |||
+ | All of the BAD colors have bit 1 set. I have no idea what the | ||
+ | significance of that is. | ||
+ | |||
+ | 3) You needed to get out of the tape load code, but you only had so many | ||
+ | keys that were still active. | ||
+ | the screen, you could break out. Since the tape load code uses CIA #1 | ||
+ | for its operations, it would take over the IC and then restore it | ||
+ | to a correct state when either the load was stopped or the load | ||
+ | | ||
+ | |||
+ | | ||
+ | have a Rev 1 ROM to try out. If someone has one, I would like to | ||
+ | have a copy of it on disk or in email. | ||
+ | information on this bug from either the May 1984 Gazette p108, or | ||
+ | from the COMPUTE! Toolkit Kernal VIC20/64, I would like a copy.) | ||
+ | |||
+ | |||
+ | Here are the answers to Commodore Trivia Edition #4 for February, 1994. | ||
+ | |||
+ | |||
+ | Q $030) On a Commodore 64, what is the amount of RAM available for BASIC | ||
+ | programs to reside in? | ||
+ | |||
+ | A $030) Some people over-answered this question. | ||
+ | 38911 bytes, which is what the BASIC screen says. Now, it is true | ||
+ | that BASIC can use $C000-$CFFF, | ||
+ | by BASIC, but it is non-trivial to get BASIC to use these areas. | ||
+ | The math comes out to: $0801 (2048) to $9FFF (40959) - 1 (0 in | ||
+ | location 2048). | ||
+ | a standard BASIC program, even if it does not use variables, since | ||
+ | BASIC steals 3 bytes at the end of the program to determine the end. | ||
+ | |||
+ | Q $031) Name one Commodore computer (pre-Amiga) that used two general purpose | ||
+ | microprocessors? | ||
+ | |||
+ | A $031) There are two (or more) answers to this question. | ||
+ | is the Commodore 128, but the Commodore SuperPET (SP9000) had two, | ||
+ | also. There was also an optional card to add another processor to | ||
+ | the B-series. | ||
+ | (or more) microprocessors, | ||
+ | |||
+ | Q $032) What are they? | ||
+ | |||
+ | A $032) Commodore 128: 8502(6510 clone) and Z80. SuperPET: 6502 and 6809. | ||
+ | B-series: 6509 and 8088. | ||
+ | |||
+ | Q $033) Who was the Chief Executive Officer of CBM when the Commodore VIC-20 | ||
+ | (VC-20) was introduced? | ||
+ | |||
+ | A $033) According to my sources, it is none other than Jack Tramiel. | ||
+ | some claim Irving Gould as the man-in-charge since he had | ||
+ | controlling interest at the time, the CEO was Jack. Whether he was | ||
+ | in charge or not is left up to the reader. | ||
+ | |||
+ | Q $034) the Commodore 64 and 128 (among others) have a TOD feature. | ||
+ | TOD stand for? | ||
+ | |||
+ | A $034) TOD = Time Of Day. The 6526 Complex Interface Adapter is the holder | ||
+ | of the TOD clock, which can be used in lieu of the system jiffy | ||
+ | system clock to time things, as it does not suffer from interruptions | ||
+ | to service I/O and screen. | ||
+ | system clock for TI and TI$, not the TOD clock. | ||
+ | |||
+ | Q $035) What location in the Commodore 64 Kernal holds the version number? | ||
+ | |||
+ | A $035) $ff80 (65408). | ||
+ | |||
+ | Q $036) The first computer Commdore sold was the KIM-1. | ||
+ | available on the KIM-1? | ||
+ | |||
+ | A $036) 1.125K or 1024+128 = 1152 bytes. | ||
+ | |||
+ | Q $037) Who designed the architecture for the 6502 integrated circuit? | ||
+ | |||
+ | A $037) Chuck Peddle | ||
+ | |||
+ | Q $038) What was the original name of the company that produced the 6502? | ||
+ | |||
+ | A $038) MOS Technologies | ||
+ | |||
+ | Q $039) What did the name stand for? | ||
+ | |||
+ | A $039) MOS = Metal Oxide Semiconductor, | ||
+ | NMOS: Negative MOS, PMOS: Positive MOS, and CMOS: Complementary MOS. | ||
+ | MOS Technologies produced mainly NMOS ICs, hence the use of NMOS | ||
+ | technology for the 6502 and 6510. | ||
+ | |||
+ | Q $03A) Commodore acquired the company and renamed it to...? | ||
+ | |||
+ | A $03A) CSG = Commodore Semiconductor Group. | ||
+ | instantaneous, | ||
+ | acquisition. | ||
+ | |||
+ | Q $03B) The Commodore VIC-20 graphics were powered by the VIC-I (6560) | ||
+ | integrated circuit. | ||
+ | the computer designed for the chip? | ||
+ | |||
+ | A $03B) The VIC-I 6560-61, was designed 2 years prior to the design of the | ||
+ | VIC-20 computer. | ||
+ | no one wanted to use it, so Commodore made their own system | ||
+ | around it to recoup losses. | ||
+ | |||
+ | Q $03C) The VIC-20 had a Video Interface Chip (VIC) inside it, yet that was | ||
+ | not what the ' | ||
+ | expand to? | ||
+ | |||
+ | A $03C) VIC-20 = Video Interface Computer-20. | ||
+ | of the amount of memory in the VIC: ~22K. Michael Tomczyk, who got | ||
+ | stuck with the job of deciding on the name, did the rounding. | ||
+ | |||
+ | Q $03D) The most widely known disk drive for Commodore computers is the 1541. | ||
+ | how much RAM does the 1541 have? | ||
+ | |||
+ | A $03D) 2048 bytes, or 2kB RAM. It is mapped at $0000-$07FF. | ||
+ | |||
+ | Q $03E) On every Commodore disk, the drive stores a copy of the BAM. What | ||
+ | does BAM stand for? | ||
+ | |||
+ | A $03E) BAM = Block Allocation Map, or Block Availability Map. I am checking | ||
+ | sources to figure out which one is the real McCoy. | ||
+ | |||
+ | Q $03F) Now, for those into 6502 machine language. | ||
+ | available on the first 6502 chips? | ||
+ | |||
+ | A $03F) ROR (ROtate Right) was not available until after June, 1976. However, | ||
+ | all Commodore VICs and C64s should have this instruction. | ||
+ | gave instructions that are found on the 65c02, designed by Western | ||
+ | Design Center, and licensed to many companies. | ||
+ | itself occurs in two flavors, and neither are used in any stock | ||
+ | Commodore product I know of. | ||
+ | |||
+ | |||
+ | Here are the answers to Commodore Trivia Edition #5 for April, 1994. | ||
+ | |||
+ | |||
+ | Q $040) The company that produces The Big Blue Reader, a program that allows | ||
+ | reading and writing of IBM formatted disk in 1571s and 1581s, is | ||
+ | called SOGWAP. | ||
+ | |||
+ | A $040) Son Of God With All Power. | ||
+ | |||
+ | Q $041) What version of DOS does the Commodore 8280 8 inch dual drive have? | ||
+ | |||
+ | A $041) The 8280 has version 3.0. Many have not ever seen this IEEE-488 | ||
+ | compatible drive used on some PETs. It has the same DOS version | ||
+ | that is in the D90XX hard drives, and could read 250kB and 500kB | ||
+ | IBM formatted disks, as well as some CP/M formats. | ||
+ | this version number is used on the 1570/71 disk drives, the code is | ||
+ | different. | ||
+ | |||
+ | Q $042) What was the color of the original Commodore 64 case? | ||
+ | |||
+ | A $042) Some early versions of the Commodore 64 were housed in VIC-20 color | ||
+ | cases, so off-white is the correct answer. | ||
+ | | ||
+ | Q $043) On an unexpanded Commodore 64, how does one read the RAM | ||
+ | locations $00 and $01? | ||
+ | |||
+ | A $043) Well, you cannot do so with the CPU directly, since it resolves these | ||
+ | locations into internal addresses. | ||
+ | addresses as external memory. | ||
+ | first bit in the sprite set, and move it over the first two bytes, | ||
+ | pretending they are part of a bitmap. | ||
+ | background collision register, you can tell if the bit in the byte is | ||
+ | set. Email me for a more complete description. | ||
+ | | ||
+ | Sven Goldt and Marko Makela get credit for this answer and the next. | ||
+ | |||
+ | Q $044) On an unexpanded Commodore 64, how does one write the same locations? | ||
+ | |||
+ | A $044) It seems the 6510 generates a valid R/W signal any time it does an | ||
+ | internal read or write. | ||
+ | internal registers were grafted onto a 6502 core processor. | ||
+ | Howevere, the address lines are also valid during any internal read | ||
+ | or write, since failure to do so may write the data on the data bus | ||
+ | to some invalid address. | ||
+ | the CPU, but from residual effects of the data last read of written by | ||
+ | the VIC chip. Thus, by programming the VIC chip to read data from | ||
+ | some known location, and by placing relevant data in that location, a | ||
+ | write to location $00 or $01 will place the data from that last read | ||
+ | VIC location into $00 or $01. This is usually accomplished by placing | ||
+ | the data to be written out into location $3fff, which the VIC fetches | ||
+ | during the time the border is being displayed. | ||
+ | routine when the raster hits the bottom border, you can copy location | ||
+ | $3fff to $00 or $01. | ||
+ | |||
+ | Q $045) What is 'CB2 Sound', | ||
+ | |||
+ | A $045) This is the sound made by sending square out of the 6522 IC on some | ||
+ | Commodore computers. | ||
+ | the pin on the 6522 that outputs the waveform. | ||
+ | complete description, | ||
+ | had the capability, and most PET owners used it as the ONLY sound | ||
+ | source, since the PETs did not have a sound chip. Although the VIC | ||
+ | did have some sound capabilities, | ||
+ | realized its widespread use and included some information on it in | ||
+ | the Commodore VIC-20 Programmer' | ||
+ | reach for your nearest VIC PRG and look at page 232. | ||
+ | |||
+ | Q $046) in question $021, the Batteries Included logo description was asked | ||
+ | for. Now, what is the name of the man in the logo? | ||
+ | |||
+ | A $046) " | ||
+ | |||
+ | Q $047) Why was the Commodore VIC-20 produced with so many 1K chips in it? | ||
+ | (Hint: it had little to do with the cost of SRAM at the time) | ||
+ | |||
+ | A $047) Jack (Tramiel) decreed that Commodore had a surplus of 1K chips, | ||
+ | so he didn't care how much memory it had, as long as the designers | ||
+ | used 1K SRAMs. | ||
+ | |||
+ | Q $048) What does ADSR stand for? | ||
+ | |||
+ | A $048) ADSR = Attack, Decay, Sustain, Release. | ||
+ | specified to define a SID waveform envelope. | ||
+ | |||
+ | Q $049) In question $035, it was learned that the Commodore 64 kernal | ||
+ | revision number is stored at $ff80 (65408). | ||
+ | stored there for: | ||
+ | |||
+ | a) The first revision? | ||
+ | b) The PET64 (4064)? | ||
+ | |||
+ | A $049) a) 170. (Yep, this was prior to 0!) | ||
+ | b) 100. (The PET 64 uses this value to adjust the startup logo | ||
+ | | ||
+ | |||
+ | Q $04A) Who was the mastermind behind the original Commodore Kernal? | ||
+ | |||
+ | A $04A) John Feagan. | ||
+ | for future computer systems. | ||
+ | modified enough with each new computer system, that the idea of | ||
+ | compatibility never really surfaced. | ||
+ | |||
+ | Q $04B) Who designed the first VIC prototype? | ||
+ | |||
+ | A $04B) There are two answers to this question. | ||
+ | name and was called the MicroPET or No Name Computer. | ||
+ | wanted to show some prototypes of the VIC at the 1980 Comsumer | ||
+ | Electronics Show (CES). | ||
+ | prototype, but TWO. Bob Yannes, working against time, had hacked | ||
+ | together a minimal working prototype using spare PET/CBM parts. | ||
+ | Another prototype, brought to the show by Bill Seiler and John | ||
+ | Feagans, had been put together after some preliminary discussions | ||
+ | with Yannes. | ||
+ | |||
+ | Q $04C) How many pins does a Commodore 1525 printhead have in it? | ||
+ | |||
+ | A $04C) Trick Question. | ||
+ | printhead has but one pin. The seven dots are created by a revolving | ||
+ | 7 sided star-wheel for the platen, which presses the paper against the | ||
+ | printhead in the seven different dot locations. | ||
+ | |||
+ | Q $04D) Why does mentioning a PET computer in France make people chuckle? | ||
+ | |||
+ | A $04D) PET means " | ||
+ | |||
+ | Q $04E) What interface IC is used to drive the IEEE-488 bus in a PET computer? | ||
+ | |||
+ | A $04E) A 6520. It is appropriately called a PIA (Peripheral Interface | ||
+ | Adapter). | ||
+ | |||
+ | 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. | ||
+ | these were available only from one source: | ||
+ | couple of years into Commodore' | ||
+ | of stock on such cables (military contract? who knows? | ||
+ | case, Commodore were in quite a fix: they made computers and disk | ||
+ | drives, but couldn' | ||
+ | order: | ||
+ | anyone can manufacture" | ||
+ | serial bus was born. It was intended to be just as fast as the | ||
+ | IEEE-488 it replaced. | ||
+ | | ||
+ | And it would have been, except dor one small glitch. | ||
+ | another trivia question. | ||
+ | |||
+ | |||
+ | Here are the answers to Commodore Trivia Edition #6 for May, 1994. | ||
+ | |||
+ | |||
+ | Q $050) The Commodore 1551 Disk Drive is a parallel device. | ||
+ | connect to the Commodore Plus/4 and C16? | ||
+ | | ||
+ | A $050) The Commodore 1551 connected via the expansion port. Therefore, it | ||
+ | was a parallel device, and could work at much faster speeds. | ||
+ | |||
+ | Q $051) How many could you attach? | ||
+ | |||
+ | A $051) Two, The second drive cable attached to the back of the first cable. | ||
+ | | ||
+ | Q $052) What were the addresses they used? (Not device numbers) | ||
+ | |||
+ | A $052) The two drives were mapped into the Address space at $fec0 and $fef0 | ||
+ | of the Plus/4 or C-16. The 6523 Triple Interface Adaptor chip is | ||
+ | mapped in at these locations and has 8 registers each. | ||
+ | | ||
+ | Q $053) What is the maximum number of sound octaves the VIC-20 sound generator | ||
+ | can reach? | ||
+ | |||
+ | A $053) This has two equally valid answers. On the Vic-20, each sound | ||
+ | generator has a range of 3 octaves. | ||
+ | together can range 5 octaves, since each sound generator is staggered | ||
+ | one octave apart. | ||
+ | | ||
+ | Q $054) Who wrote the reference guide that was distributed with almost every | ||
+ | PET computer sold? | ||
+ | |||
+ | A $054) The infamous Adam Osborne, of Osborne I fame. | ||
+ | | ||
+ | Q $055) The box that the C64 comes in has some propaganda on the side | ||
+ | describing the unit. In the specifications section, it claims how | ||
+ | many sprites can be on screen at one time? | ||
+ | |||
+ | A $055) I neglected to note that the Commodore 64 packing box has underwent | ||
+ | many changes. | ||
+ | many views of the 64, and a specification list on on side of the box. | ||
+ | On that spec list, it claims that the the 64 can have " | ||
+ | independently controlled objects, 8 on one line." | ||
+ | important? | ||
+ | would and could use the interrrupts on the VIC-II to change sprite | ||
+ | pointers. | ||
+ | | ||
+ | Q $056) The Commodore Plus/4 computer contained the first integrated software | ||
+ | package to be placed in a personal computer. | ||
+ | software package? | ||
+ | |||
+ | A $056) The package was called " | ||
+ | | ||
+ | Q $057) What popular computer software did the software package parody? | ||
+ | |||
+ | A $057) Lotus 1-2-3. | ||
+ | | ||
+ | Q $058) One familiar Commodore portable computer was called the SX-64. | ||
+ | What did SX really stand for? | ||
+ | |||
+ | A $058) Depending on whom you believe, the SX stands for two things. | ||
+ | choose to believe Jack Tramiel, the SX stands for " | ||
+ | has been quoted as saying, " | ||
+ | involved" | ||
+ | names of the computers. | ||
+ | marketing version. | ||
+ | portable 64 was called the Executive 64. There was to have been a DX | ||
+ | model, which would have had two drives. | ||
+ | | ||
+ | Q $059) Who (what person) invented the Sound Interface Device (SID) chip? | ||
+ | |||
+ | A $059) Bob Yannes, who also worked on one of the VIC prototypes, developed | ||
+ | this chip. | ||
+ | | ||
+ | Q $05A) The ill-fated UltiMax (later called the MAX Machine) contained a | ||
+ | number of Commodore 64 features. | ||
+ | feature of 64kB RAM. How much RAM did the MAX have? | ||
+ | |||
+ | A $05A) A whopping 2 kilobytes. | ||
+ | memory dropped to .5 kilobyte or 512 bytes. | ||
+ | this one. | ||
+ | | ||
+ | Q $05B) What famous person was featured in U.S. television advertising for | ||
+ | the VIC-20? | ||
+ | |||
+ | A $05B) William Shatner. | ||
+ | He was not, however, in uniform, since CBM did not have rights to | ||
+ | Star Trek of any sort. | ||
+ | | ||
+ | Q $05C) What company designed the first VICModem? | ||
+ | |||
+ | A $05C) Anchor Automation. | ||
+ | the VICModem was designed to be sold for under $100 when most were | ||
+ | $400 or more. The secret to the cost containment was the ability to | ||
+ | use what we soetimes think of as a disadvantage of the User Port to | ||
+ | the modem' | ||
+ | be buffered before driving the modem, and the +5 volt power available | ||
+ | through the User Port just was not available through normal RS-232 | ||
+ | lines. | ||
+ | extra components that would have increased case size and cost, and not | ||
+ | having the on-board power would have meant a power connector and power | ||
+ | supply would need to be bundled. | ||
+ | the first VICModem, I can tell you it was worth the hassle. | ||
+ | | ||
+ | Q $05D) Everyone has seen or heard of BYTE Magazine. | ||
+ | articles in the 80's, and coverage of PC products in the 90's, BYTE | ||
+ | was founded by Wayne Green. | ||
+ | Wayne Green later publish? | ||
+ | |||
+ | A $05D) RUN Magazine. | ||
+ | | ||
+ | Q $05E) (Three part question) What are the official names of the colors | ||
+ | used on the VIC-20: | ||
+ | |||
+ | a) case? | ||
+ | b) regular typewriter keys? | ||
+ | c) function keys? | ||
+ | |||
+ | A $05E) a) ivory. | ||
+ | b) chocolate brown. | ||
+ | c) mustard. | ||
+ | | ||
+ | Q $05F) Commodore is set up as a ___________ chartered company. | ||
+ | the missing country. | ||
+ | |||
+ | A $05F) Bahamas. | ||
+ | the Bahamas as low as 1%, more money could be kept from the | ||
+ | governments. | ||
+ | |||
+ | |||
+ | Here are the answers to Commodore Trivia Edition #7 for May, 1994. | ||
+ | |||
+ | |||
+ | Q $060) When you turn on stock Commodore 16, how many bytes free does it | ||
+ | report? | ||
+ | |||
+ | A $060) According to the initial power-up indication on the monitor, a stock | ||
+ | Commodore 16 has 12277 bytes free for BASIC program use. A number od | ||
+ | people have calculated 12287 bytes, so the power-on message may be in | ||
+ | error. | ||
+ | |||
+ | Q $061) How many does a stock Plus/4 report? | ||
+ | |||
+ | A $061) According to its initial power-up message, the Plus/4 has 60671 | ||
+ | bytes free. | ||
+ | |||
+ | Q $062) What was the VIC-20' | ||
+ | |||
+ | A $062) "The Friendly Computer" | ||
+ | |||
+ | Q $063) What personality announced the birth of the Commodore 64 in | ||
+ | Christmas advertisements? | ||
+ | |||
+ | A $063) Though not well-known outside of the US, Henry Morgan introduced the | ||
+ | new Commodore 64 computer system in the US. In other countries, the | ||
+ | answers differ, as countries like Finland had the Statue of Liberty | ||
+ | announce the C64 birth. | ||
+ | |||
+ | Q $064) What was the name of the monitor program included in the Plus/4? | ||
+ | |||
+ | A $064) TEDMon. | ||
+ | |||
+ | Q $065) How many sectors per track are there for tracks 1-17 on a 1541? | ||
+ | |||
+ | A $065) 21. | ||
+ | |||
+ | Q $066) There are two programs running in the Commodore single-6502 drives | ||
+ | (1541, | ||
+ | |||
+ | A $066) The interpreter program is called the Interface Processor (IP). It | ||
+ | handles the dispatching of all commands sent to the drive, as well | ||
+ | as corrdinating the flow of traffic between the disk and the computer. | ||
+ | |||
+ | Q $067) How do you do a hard reset on a Plus/4 ? | ||
+ | |||
+ | A $067) First, we need to define hard-reset. | ||
+ | cycle, since the latter does not retain the RAM contents. | ||
+ | case, the answer is analogous to the RUN/ | ||
+ | found on the 64 and VIC-20. | ||
+ | recessed reset button on the side of the computer. | ||
+ | | ||
+ | | ||
+ | Q $068) Where did the name " | ||
+ | |||
+ | A $068) Rumor has it that Jack Tramiel always wante to use a naughtical term, | ||
+ | but most had been already used. However, one day he watched a moving | ||
+ | company van pass by on the street with the name he decided to use as | ||
+ | soon as he saw it: Commodore. | ||
+ | |||
+ | Q $069) Chuck Peddle, designer of the 6502, left Commodore twice. Where did he | ||
+ | go first? | ||
+ | |||
+ | A $069) He went to Apple Computer. | ||
+ | that Apple and Chuck got along even worse than Commodore and Chuck. | ||
+ | |||
+ | Q $06A) Where did he eventually go when he left for good? | ||
+ | |||
+ | A $06A) First, he went off to start a company called Sirius, which died almost | ||
+ | before it started due to a lawsuit over the name. Then, he and some | ||
+ | former Commodore designers came up with the " | ||
+ | did modestly, but never took off. | ||
+ | |||
+ | Q $06B) What does the Kernal routine at $FFD2 do in terms of function and what | ||
+ | parameters get passed and returned? | ||
+ | |||
+ | A $06B) The KERNAL routine at $FFD2 on all Commodore 8 bit machines outputs the | ||
+ | PETSCII character code contained in the .A register to the current | ||
+ | output device. | ||
+ | return. | ||
+ | |||
+ | Q $06C) What Commodore drive has a hidden message? | ||
+ | |||
+ | A $06C) The 1581 has a couple such hidden messages. | ||
+ | IP, the text says "am i lazy??? | ||
+ | Also, in the same loop, the following can be found: "this is lazy!!!" | ||
+ | Lastly, the credits in the 1581 roms are: " | ||
+ | hardware greg berliNZDedicatedto my wife lisA" | ||
+ | and the A in lisA is typical of how strings are stored in the 1581, | ||
+ | last byte has bit 7 set. The Z after berliN appears to have been a | ||
+ | typo, but I can't say for sure. I have a program that displays these. | ||
+ | (Email me for info.) | ||
+ | | ||
+ | The 1571 has the ROM authors' | ||
+ | ROM, but I don't have a 1571 to scan for them. | ||
+ | |||
+ | Q $06D) What computer was the first to have a hidden message? | ||
+ | |||
+ | A $06D) The PET 2001. Some said the 128 has a hidden message, but it wasn't | ||
+ | the first. | ||
+ | |||
+ | Q $06E) What was it and how did you get it to come up? | ||
+ | |||
+ | A $06E) By typing: | ||
+ | wait 6502, | ||
+ | the computer printed Microsoft! x times on the screen. | ||
+ | |||
+ | Q $06F) What does NTSC stand for? | ||
+ | |||
+ | A $06F) Truthfully, NTSC can stand for different things. | ||
+ | television standard for the US, the expansion is National Television | ||
+ | Standard Code. However, the body that formed the standard is also | ||
+ | called NTSC: National Television System Committee. | ||
+ | | ||
+ | |||
+ | Commodore Trivia Edition #8 | ||
+ | |||
+ | | ||
+ | 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? | ||
+ | |||
+ | Q $071) The IEEE-488 interface is sometimes called the GPIB interface. | ||
+ | What does GPIB stand for? | ||
+ | |||
+ | Q $072) Commodore manufactured at least two hard drives with IEEE-488 | ||
+ | interfaces. | ||
+ | |||
+ | Q $073) Why didn't buyers like the original PET-64? | ||
+ | |||
+ | Q $074) On a PET Revision 2 ROM, what was the largest single array size that | ||
+ | BASIC could handle? | ||
+ | |||
+ | 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? | ||
+ | |||
+ | Q $076) On all Commodore floppy disk drives, how fast does the disk spin? | ||
+ | | ||
+ | Q $077) Upon first reading the Commodore 1541 Error channel after turning | ||
+ | on the disk drive, what error number and text is returned? | ||
+ | |||
+ | Q $078) What error number and text is returned on a 1551? | ||
+ | |||
+ | Q $079) Commodore printers are normally assigned to device #4, but they | ||
+ | can be also used as device #? | ||
+ | |||
+ | Q $07A) What microprocessor is used in the Commodore 1551 disk drive? | ||
+ | |||
+ | 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? | ||
+ | | ||
+ | Q $07C) On Commodore computers, how much RAM is set aside as a tape buffer? | ||
+ | |||
+ | Q $07D) On Commodore computers, most every peripheral has a device number. | ||
+ | What is the device number of the screen? | ||
+ | |||
+ | Q $07E) What is the device number of the keyboard? | ||
+ | |||
+ | Q $07F) Commodore computers use 2' | ||
+ | What is the 2' | ||
+ | |||
+ | Some are easy, some are hard, try your hand at: | ||
+ | |||
+ | Commodore Trivia Edition #8! | ||
+ | |||
+ | Jim Brain | ||
+ | brain@mail.msen.com | ||
+ | 2306 B Hartland Road | ||
+ | Hartland, MI 48353 | ||
+ | (810) 737-7300 x8528 | ||
+ | |||
+ | ============================================================================= | ||
+ | </ | ||
+ | ====== RS232 Converter ====== | ||
+ | < | ||
+ | by Walter Wickersham (shadow@connected.com) | ||
+ | |||
+ | [Editor' | ||
+ | it because I _do_ think you can get away with it... However, because this | ||
+ | magazine is free you get what you pay for... ] | ||
+ | |||
+ | Here's a modem interface schematic for the C=64/128, with it, and around | ||
+ | $5.00, you can use almost any hayes compat. external modem. | ||
+ | my knowedge, the 64 has a maximum baud rate (through the user port) of 2400, | ||
+ | and the 128's is 9600. | ||
+ | |||
+ | I DO NOT know who the original author of this is, but i re-wrote it in my | ||
+ | own words, hoping it will help someone. I CLAIM NO RIGHTS TO THIS ARTICLE. | ||
+ | |||
+ | PARTS LIST: | ||
+ | ------------- | ||
+ | 7404 Hex Inverter IC ($0.99 at Radio Shack) | ||
+ | Wires, solder, etc. | ||
+ | Commodore User port connector (I used one off a old 1650) | ||
+ | |||
+ | Here It is: | ||
+ | C64/128 USER PORT RS232 ADAPTER | ||
+ | |||
+ | A & N -----------------------GROUND---------------------- 1 & 7 | ||
+ | B & C ---------------------2-7404 | ||
+ | | ||
+ | M -------------------------3-7404 | ||
+ | | ||
+ | H-------------------------------------------------------- 8 | ||
+ | E-------------------------------------------------------- 20 | ||
+ | K ------------------------------------------------------- 5 | ||
+ | L ------------------------------------------------------- 6 | ||
+ | |||
+ | Pin #2n the user port MUST be connected to pin 14 of the 7404. | ||
+ | Pins A&N (ground) MUST be connected to pin 7 of the 7404. | ||
+ | |||
+ | For those of you who don't have a pinout of the user port, here, have one. | ||
+ | (TOP) | ||
+ | | ||
+ | | ||
+ | | ||
+ | (BOTTOM) | ||
+ | |||
+ | THIS DOES WORK, that's why i'm modeming at 2400. :->, but i sometimes | ||
+ | recieve line noise, so any upgrades to this would be appreciated (i know | ||
+ | it's not my phone line). | ||
+ | |||
+ | ============================================================================= | ||
+ | </ | ||
+ | ====== Programming the Commodore RAM Expansion Units (REUs) ====== | ||
+ | < | ||
+ | by Richard Hable (Richard.Hable@jk.uni-linz.ac.at) | ||
+ | |||
+ | The following article, initially written for a mailing list, describes | ||
+ | the Commodore REUs and explanes how to program them. | ||
+ | |||
+ | Contents: | ||
+ | |||
+ | 1) External RAM Access With REUs | ||
+ | 2) RAM Expansion Controller (REC) Registers | ||
+ | 3) How To Recognize The REU | ||
+ | 4) Simple RAM Transfer | ||
+ | 5) Additional Features | ||
+ | 6) Transfer Speed | ||
+ | 7) Interrupts | ||
+ | 8) Executing Code In Expanded Memory | ||
+ | 9) Other Useful Applications Of The REU | ||
+ | 10) Comparision Of Bank Switching and DMA | ||
+ | |||
+ | |||
+ | 1) _External RAM Access With REUs_ | ||
+ | |||
+ | The REUs provide additional RAM for the C64/ | ||
+ | been produced by Commodore. | ||
+ | and 512 KBytes built in RAM. However, they can be extended up to several | ||
+ | MBytes. | ||
+ | |||
+ | The external memory can not be directly addressed by the C64 with its 16 bit | ||
+ | address space--it has to be transferred from and to the main memory of the | ||
+ | C64. For that purpose, there is a built in RAM Expansion Controller (REC) | ||
+ | which transfers memory between the C64 and the REU using Direct Memory Access | ||
+ | (DMA). | ||
+ | |||
+ | |||
+ | 2) _RAM Expansion Controller (REC) Registers_ | ||
+ | |||
+ | The REC is programmed by accessing its registers. | ||
+ | through the expansion port, these registers appear memory mapped in the | ||
+ | I/O-area between $DF00 and $DF0A. | ||
+ | and SID-registers. | ||
+ | |||
+ | $DF00: STATUS REGISTER | ||
+ | Various information can be obtained (read only). | ||
+ | |||
+ | Bit 7: | ||
+ | | ||
+ | Bit 6: END OF BLOCK (1 = transfer complete) | ||
+ | | ||
+ | Bit 5: | ||
+ | set if a difference between C64 and REU memory areas | ||
+ | was found during a compare command | ||
+ | Bit 4: | ||
+ | seems to indicate the size of the RAM-chips; | ||
+ | set on 1764 and 1750, clear on 1700. | ||
+ | Bits 3..0: VERSION | ||
+ | | ||
+ | |||
+ | $DF01: COMMAND REGISTER | ||
+ | By writing to this register, RAM transfer or comparision can be | ||
+ | | ||
+ | |||
+ | Bit 7: | ||
+ | must be set to execute a command | ||
+ | Bit 6: | ||
+ | Bit 5: | ||
+ | With autoload enabled, the address and length registers (see | ||
+ | | ||
+ | | ||
+ | | ||
+ | and the length register will be changed (normally to 1). | ||
+ | Bit 4: FF00 | ||
+ | If this bit is set, command execution starts immediately | ||
+ | after setting the command register. | ||
+ | | ||
+ | | ||
+ | Bits 3..2: reserved | ||
+ | Bits 1..0: TRANSFER TYPE | ||
+ | 00 = transfer C64 -> REU | ||
+ | 01 = transfer REU -> C64 | ||
+ | 10 = swap C64 <-> REU | ||
+ | 11 = compare C64 - REU | ||
+ | |||
+ | $DF02..$DF03: | ||
+ | 16-bit C64 base address in low/high order | ||
+ | |||
+ | $DF04..$DF06: | ||
+ | This is a three byte address, consisting of a low and | ||
+ | high byte and an expansion bank number. | ||
+ | Normally, only bits 2..0 of the expansion bank are valid | ||
+ | (for a maximum of 512 KByte), the other bits are always | ||
+ | set. | ||
+ | |||
+ | $DF07..$DF08: | ||
+ | This is a 16 bit value containing the number of bytes to | ||
+ | transfer or compare. | ||
+ | The value 0 stands for 64 KBytes. | ||
+ | If the transfer length plus the C64 base address exceeds | ||
+ | 64K, the C64 address will overflow and cause C64 memory | ||
+ | from 0 on to be accessed. | ||
+ | If the transfer length plus the REU base address exceeds | ||
+ | 512K, the REU address will overflow and cause REU memory | ||
+ | from 0 on to be accessed. | ||
+ | |||
+ | $DF09: INTERRUPT MASK REGISTER | ||
+ | unnecessary | ||
+ | |||
+ | Bit 7: | ||
+ | Bit 6: END OF BLOCK MASK (1 = interrupt on end) | ||
+ | Bit 5: | ||
+ | Bits 4..0: unused (normally all set) | ||
+ | |||
+ | $DF0A: ADDRESS CONTROL REGISTER | ||
+ | With this register, address counting during DMA can be controlled. | ||
+ | If a base address is fixed, the same byte is used repeatedly. | ||
+ | |||
+ | Bit 7: C64 ADDRESS CONTROL | ||
+ | Bit 6: REU ADDRESS CONTROL | ||
+ | Bits 5..0: unused (normally all set) | ||
+ | |||
+ | |||
+ | In order to access the REU registers in assembly language, it is convenient | ||
+ | to define labels something like this: | ||
+ | |||
+ | status | ||
+ | command | ||
+ | c64base | ||
+ | reubase | ||
+ | translen = $DF07 | ||
+ | irqmask | ||
+ | control | ||
+ | |||
+ | |||
+ | 3) _How To Recognize The REU_ | ||
+ | |||
+ | Normally, the addresses between $DF02 and $DF05 are unused, values stored | ||
+ | there get lost. Therefore, if e.g. the values 1,2,3,4 are written to | ||
+ | $DF02..$DF05 and do not stay there, no REU can be connected. | ||
+ | values are there, it could be caused by another kind of module connected that | ||
+ | also uses these addresses. | ||
+ | |||
+ | Another problem is the recognition of the number of RAM banks (64 KByte | ||
+ | units) installed. | ||
+ | 4 (1764, 1750) banks installed. | ||
+ | many RAM banks as possible, the real size can be determined. | ||
+ | seen in the source to " | ||
+ | Hacking Issue 2. | ||
+ | |||
+ | In any way, the user of a program should be able to choose, if and which REU | ||
+ | banks are to be used. | ||
+ | |||
+ | |||
+ | 4) _Simple RAM Transfer_ | ||
+ | |||
+ | Very little options of the REU are necessary for the main purposes of RAM | ||
+ | expanding. | ||
+ | command register. | ||
+ | |||
+ | The following code transfers one KByte containing the screen memory | ||
+ | ($0400..$07FF) to address 0 in the REU: | ||
+ | |||
+ | lda #0 | ||
+ | sta control ; to make sure both addresses are counted up | ||
+ | lda #<$0400 | ||
+ | sta c64base | ||
+ | lda #>$0400 | ||
+ | sta c64base + 1 | ||
+ | lda #0 | ||
+ | sta reubase | ||
+ | sta reubase + 1 | ||
+ | sta reubase + 2 | ||
+ | lda #<$0400 | ||
+ | sta translen | ||
+ | lda #>$0400 | ||
+ | sta translen + 1 | ||
+ | lda # | ||
+ | sta command | ||
+ | |||
+ | In order to transfer the memory back to the C64, replace "lda # | ||
+ | "lda # | ||
+ | |||
+ | I think, this subset of 17xx functions would be enough for a reasonable RAM | ||
+ | expansion. | ||
+ | the more complicated functions have to be implemented. | ||
+ | |||
+ | 5) _Additional Features_ | ||
+ | |||
+ | Swapping Memory | ||
+ | |||
+ | With the swap-command, | ||
+ | programming is the same as in simple RAM transfer. | ||
+ | |||
+ | |||
+ | Comparing Memory | ||
+ | |||
+ | No RAM is transferred. Instead, the number of bytes specified in the transfer | ||
+ | length register is compared. | ||
+ | status register is set. In order to get valid information, | ||
+ | be cleared before comparing. | ||
+ | register. | ||
+ | |||
+ | |||
+ | Using All C64 Memory | ||
+ | |||
+ | Normally, C64 memory is accessed in the memory configuration selected during | ||
+ | writing to the command register. | ||
+ | register, the I/O-area has to be active. | ||
+ | character ROM shall be used, it is possible to delay the execution of the | ||
+ | command by using a command byte with bit 4 (" | ||
+ | will then be executed when an arbitrary value is written to address $FF00. | ||
+ | |||
+ | Example: | ||
+ | |||
+ | < Set base addresses and transfer length > | ||
+ | lda #%10000000 ; transfer C64 RAM -> REU delayed | ||
+ | sta command | ||
+ | sei | ||
+ | lda $01 | ||
+ | and #$30 | ||
+ | sta $01 ; switch on 64 KByte RAM | ||
+ | lda $FF00 ; do not change the contents of $FF00 | ||
+ | sta $FF00 ; execute DMA | ||
+ | lda $01 | ||
+ | ora #$37 | ||
+ | sta $01 ; switch on normal configuration | ||
+ | cli | ||
+ | |||
+ | |||
+ | 6) _Transfer Speed_ | ||
+ | |||
+ | During DMA the CPU is halted--the memory access cycles normally available for | ||
+ | the CPU are now used to access one byte each cycle. Therefore, with screen | ||
+ | and sprites switched off, in every clock cycle (985248 per second on PAL | ||
+ | machines) one byte is transferred. | ||
+ | transfer is a bit slower, as the VIC sometimes accesses RAM exclusively. | ||
+ | Comparing memory areas is as fast as transfering. | ||
+ | once the first difference is found.) | ||
+ | because two C64 memory accesses per byte (read & write) are necessary. | ||
+ | |||
+ | |||
+ | 7) _Interrupts_ | ||
+ | |||
+ | By setting certain bits in the interrupt mask register, IRQs at the end of a | ||
+ | DMA can be selected. | ||
+ | comparision will always be finished after the store instruction into the | ||
+ | command register or $FF00. | ||
+ | OF BLOCK" (bit 6 of status register) or to enable an interrupt. | ||
+ | |||
+ | |||
+ | 8) _Executing Code In Expanded Memory_ | ||
+ | |||
+ | Code in expanded memory has to be copied into C64 memory before execution. | ||
+ | This is a disadvantage against bank switching systems. However, bank | ||
+ | switching can be simulated by the SWAP command. | ||
+ | There, only 256 bytes of C64 memory are occupied, the 8 KByte RAM disk driver | ||
+ | is swapped in whenever needed. | ||
+ | be relatively slow at sequential file access. | ||
+ | |||
+ | |||
+ | 9) _Other Useful Applications Of The REU_ | ||
+ | |||
+ | The REC is not only useful for RAM transfer and comparison. | ||
+ | |||
+ | One other application (used in GEOS) is copying C64 RAM areas by first | ||
+ | transferring them to the REU and then transferring them back into the desired | ||
+ | position in C64 memory. | ||
+ | than copying memory with machine language instructions. | ||
+ | |||
+ | Interesting things can be done by fixing base addresses: | ||
+ | base address, large C64 areas can be fast filled with a single byte value. | ||
+ | It is also possible to find the end of an area containing equal bytes very | ||
+ | fast, e.g. for data compression. | ||
+ | |||
+ | Fixing the C64 base address is interesting if it points to an I/ | ||
+ | Then, data can be written out faster than normally possible. | ||
+ | possible to use real bitmap graphics in the upper and lower screen border by | ||
+ | changing the "magic byte" (byte with the highest address accessable by the | ||
+ | VIC) in every clock cycle. Therefore, of course, the vertical border would | ||
+ | have to be switched off. | ||
+ | |||
+ | Generally the REC could be used as graphics accelerator, | ||
+ | areas or other display data fast into the VIC-addressable 16 KByte area. | ||
+ | |||
+ | |||
+ | 10) _Comparision Of Bank Switching and DMA_ | ||
+ | |||
+ | When comparing bank switching and DMA for memory expansion, I think, DMA is | ||
+ | the more comfortable method to program. It is also faster in most cases. | ||
+ | The disadvantage of code execution not possible in external memory can be | ||
+ | minimized by always copying only the necessary parts into C64 memory. | ||
+ | Executing the code will then take much more time than copying it into C64 | ||
+ | memory. | ||
+ | |||
+ | ============================================================================= | ||
+ | </ | ||
+ | ====== A Different Perspective: | ||
+ | < | ||
+ | by Stephen Judd (judd@merle.acns.nwu.edu) and | ||
+ | | ||
+ | |||
+ | Introduction | ||
+ | ------------ | ||
+ | |||
+ | We've all seen them: neat-looking three-dimensional graphics tumbling around | ||
+ | on a computer. | ||
+ | Commodore-64? | ||
+ | use the functions in 3dgrphcs.lib" | ||
+ | second is either "Well an elite coder like me can't let secrets like that | ||
+ | out" or else "What, you mean people still use those things?" | ||
+ | |||
+ | So this is a little article which attempts to take some of the mystery out | ||
+ | of three dimensional graphics. | ||
+ | simple, and the geometric concepts are very intuitive. Coding it up on a | ||
+ | C-64 is more of a challenge, especially when you want to make it fast, but | ||
+ | even then it's not too tough. | ||
+ | (and talked about it for about a week before that). | ||
+ | appreciate this aspect more if you know that I haven' | ||
+ | since 1988, and until the last two days George had no computer on which to | ||
+ | test his ideas (and on the last day it died)! | ||
+ | |||
+ | The goal of this article is that by the time you reach the end of it you | ||
+ | will be able to do your own cool-looking 3d graphics on a C64. Some of you | ||
+ | may find it patronizing at times, but I hope that it is at a level that | ||
+ | everyone can enjoy and learn something from. And feel free to write to us! | ||
+ | |||
+ | The first part explains some of the fundamental theoretical concepts | ||
+ | involved. | ||
+ | you need to know a little trigonometry, | ||
+ | matrices together. | ||
+ | |||
+ | The second part deals with implementing the algorithms on a computer; since | ||
+ | this is C=Hacking, it is a good assumption that the implementation is on the | ||
+ | C-64! Most of the code is designed for speed, and lots of it is also | ||
+ | designed so that it can be called from BASIC! | ||
+ | |||
+ | Finally, an example program which uses all of the techniques presented here | ||
+ | is included, including source. | ||
+ | dimensions. | ||
+ | |||
+ | By itself the code is not the fastest in the world; what is important here | ||
+ | are the concepts. | ||
+ | you can get these routines going quite fast; for instance, a 26 cycle line | ||
+ | drawing routine is not hard at all using more sophisticated versions of | ||
+ | these algorithms. | ||
+ | quality. | ||
+ | |||
+ | There are lots and lots of little details that are not specifically covered | ||
+ | by this article. | ||
+ | shouldn' | ||
+ | |||
+ | This material is the result of a week's worth of discussions on comp.sys.cbm | ||
+ | between George, myself, and several other people. | ||
+ | everyone who helped us to knock these ideas out, and we hope you find this | ||
+ | to be a useful reference! | ||
+ | |||
+ | Incidentally, | ||
+ | drawing neat pictures; for example, a good application is the stabilization | ||
+ | of an orbiting satellite. | ||
+ | transformations are important in, for instance, the study of dynamical | ||
+ | systems (which leads to Chaos and all sorts of advanced mathematical | ||
+ | subjects). | ||
+ | |||
+ | But it also makes you look really cool in front of your friends. | ||
+ | |||
+ | First Things First | ||
+ | ------------------ | ||
+ | |||
+ | Before we begin, you are going to have to get a few ideas into your head. | ||
+ | First and foremost is the coordinate system we will be using: a right-handed | ||
+ | coordinate system. | ||
+ | you, the y-axis is going to go to your right, and the z-axis is going to go | ||
+ | " | ||
+ | |||
+ | Second, you need to know a little math. You need to know about polar | ||
+ | coordinates, | ||
+ | The ideas are all geometric, but the computations are all (of course) | ||
+ | mathematical. | ||
+ | |||
+ | Now, let us start thinking about a cube! | ||
+ | |||
+ | Let's first center our cube at the origin. | ||
+ | to visualize, but to make our cube do things (like rotate) the way we want | ||
+ | it to we are going to have to require this. A cube has eight corners, all | ||
+ | connected to each other in a particular way. | ||
+ | |||
+ | There' | ||
+ | corners of the cube at x=+/-1, y=+/-1, and z=+/ | ||
+ | points to work with: P1=[1 1 1] P2=[1 -1 1] P3=[-1 -1 1] etc. | ||
+ | |||
+ | Minimalists may disagree, but a cube all by itself isn't all that exciting. | ||
+ | So how do we do stuff to it? For that matter, what kinds of stuff can we do | ||
+ | to it? | ||
+ | |||
+ | Rotations in the Plane | ||
+ | ---------------------- | ||
+ | |||
+ | One of the cool things to do with a three-dimensional object is of course to | ||
+ | rotate it. To understand how to do this, we need to first look at rotations | ||
+ | in the plane. | ||
+ | how to multiply two matrices together. | ||
+ | |||
+ | Before starting, we need to know some important trig formulas (of course, | ||
+ | everyone knows important formulas like these, but let me just remind you of | ||
+ | them): | ||
+ | |||
+ | cos(A+B) = cos(A)cos(B) - sin(A)sin(B) | ||
+ | sin(A+B) = cos(A)sin(B) + sin(A)cos(B) | ||
+ | |||
+ | Let us take a look at rotations in the plane; that is, in two dimensions. | ||
+ | Think of the typical x-y axis. Let's say that we have a point at [x1 y1] | ||
+ | and want to rotate it by an angle B, about the origin, so that we end up at | ||
+ | the rotated coordinate [x2 y2]. What are x2 and y2? The easiest way to | ||
+ | find them is to use polar coordinates. | ||
+ | |||
+ | We can write the point [x1 y1] as the point (r,t), where r is the distance | ||
+ | from the origin to the point, and t is the angle from the x-axis, measured | ||
+ | counter-clockwise. | ||
+ | rotate this vector by an amount B, | ||
+ | |||
+ | x2 = r*cos(t+B) | ||
+ | = r*(cos(t)cos(B) - sin(t)sin(B)) | ||
+ | = x1*cos(B) - y1*sin(B). | ||
+ | |||
+ | Similarly, | ||
+ | |||
+ | y2 = r*sin(t+B) = x1*sin(B) + y1*cos(B). | ||
+ | |||
+ | In matrix form, this can be written as | ||
+ | |||
+ | [x2] = [cos(B) | ||
+ | [y2] | ||
+ | |||
+ | How do we extend this to three dimensions? | ||
+ | here is that, in three dimensions, the above rotations are really rotations | ||
+ | about the z-axis. | ||
+ | of the three-dimensional space (so that our slice is parallel to the x-y | ||
+ | axis) and pretend that we are really in two-dimensional space. | ||
+ | to rotate a point about the z-axis the x- and y-equations are the same as | ||
+ | above, and the z-coordinate stays fixed. | ||
+ | |||
+ | [x2] | ||
+ | [y2] = [sin(B) | ||
+ | [z2] | ||
+ | |||
+ | Similarly, it is easy to see that | ||
+ | |||
+ | [x2] | ||
+ | [y2] = [ 0 cos(B) | ||
+ | [z2] | ||
+ | |||
+ | is a rotation about the x-axis, and that | ||
+ | |||
+ | [x2] | ||
+ | [y2] = [ 0 1 0 ] [y1] | ||
+ | [z2] | ||
+ | |||
+ | is a rotation about the y-axis. | ||
+ | sin(B) have been reversed; this is because in our right-handed coordinate | ||
+ | system the z-x plane is " | ||
+ | left, while z increases " | ||
+ | |||
+ | You may be wondering why we write this all in matrix form. The above matrix | ||
+ | equations are called linear transformations of the vector [x1 y1 z1]. There | ||
+ | are lots of deep mathematical concepts sitting right behind what looks to be | ||
+ | an easy way of writing several equations. | ||
+ | subject, and that is as good a reason as any for me not to go into detail. | ||
+ | |||
+ | But writing things in this way also offers us several _computational_ | ||
+ | advantages. | ||
+ | that I want to rotate a point about the x-axis, shear it in the y-direction, | ||
+ | reflect it through the line theta=pi/5, and rotate it through the z-axis. | ||
+ | You could have one subroutine which did the rotation, and one that did the | ||
+ | shear, etc. But by writing it in matrix form, the entire process is simply | ||
+ | a series of matrix multiplications. | ||
+ | |||
+ | If you think about it you might realize that it really is the same thing no | ||
+ | matter which way you do it, but there is a fundamental difference in the | ||
+ | viewpoint of each method: one views it as a series of unrelated mathematical | ||
+ | operations each with it's own individual function, while the other method | ||
+ | views it as a series of matrix multiplications so that it's basically the | ||
+ | same thing, over and over. | ||
+ | |||
+ | What this means for you is that if you want to rotate a point around the | ||
+ | x-axis, the y-axis, and the z-axis, you can take the matrix for each | ||
+ | transformation and multiply them all together, and then apply this one big | ||
+ | matrix to the point. | ||
+ | multiplication is not commutative. | ||
+ | the x-axis and Y is a rotation about the y-axis, it will almost never be | ||
+ | true that XY = YX. What this means geometrically is that if you take a | ||
+ | point, rotate it around the x-axis by an amount A, then rotate it around the | ||
+ | y-axis by an amount B, you will usually end up at a different point than if | ||
+ | you first rotate it around the y-axis. | ||
+ | |||
+ | If you are interested in learning more about rotations and their uses, a | ||
+ | good place to start is almost any book on mechanics, for instance " | ||
+ | Mechanics" | ||
+ | transformations you can find it in any decent book on linear algebra, as | ||
+ | well as in a lot of physics texts. | ||
+ | and Murphy "The Mathematics of Physics and Chemistry", | ||
+ | semi-OK book on linear algebra by Goldberg. | ||
+ | |||
+ | Now we know the geometric and mathematical principles behind rotations in | ||
+ | three dimensions. | ||
+ | two-dimensional screen: we need some way of taking a point in | ||
+ | three-dimensions and bringing it down to two dimensions, but in a way that | ||
+ | fools us into thinking that it really is three-dimensional. | ||
+ | |||
+ | What we need are projections. | ||
+ | |||
+ | Projections | ||
+ | ----------- | ||
+ | |||
+ | Now, we could just do a simple projection and set the z-coordinate equal to | ||
+ | zero, but in doing so we have eliminated some of the information, | ||
+ | won't look very three-dimensional to our eyes. So we need to think of a | ||
+ | better method. | ||
+ | |||
+ | Sit back in your chair and imagine for a minute or two. Imagine the three | ||
+ | coordinate axes. Now imagine that there is a pinhole camera, with it's | ||
+ | pinhole lens at the origin, and it's film at the plane at z=1 parallel to | ||
+ | the x-y plane. | ||
+ | |||
+ | Maybe a little picture would help: | ||
+ | |||
+ | | | ||
+ | | | ||
+ | /| | ||
+ | lens / |film | ||
+ | -----*--|------------ z-axis | ||
+ | / | | ||
+ | / | ||
+ | / z=1 | ||
+ | | ||
+ | |||
+ | What does this object look like on the film? | ||
+ | |||
+ | Let's say one of the points of this something is [x y z]. Where does this | ||
+ | point come out on the film? Since the lens is at the origin, we want to | ||
+ | draw the line from [x y z] through the origin (since that's where our lens | ||
+ | is) and find the point [x1 y1 1] where it hits the film. The parametric | ||
+ | equation of this line is | ||
+ | |||
+ | t * [x y z] | ||
+ | |||
+ | so that we want to find the intersection of this line and the film: | ||
+ | |||
+ | t * [x y z] = [x1 y1 1]. | ||
+ | |||
+ | The z-coordinate tells us that t*z=1, or t=1/ | ||
+ | in the above equation, we find that | ||
+ | |||
+ | x1 = x/z y1 = y/z | ||
+ | |||
+ | If, instead of placing the film at z=1 we place it at z=d, we get | ||
+ | |||
+ | x1 = d*x/z y1 = d*y/z | ||
+ | |||
+ | These then are the projection equations. | ||
+ | changing d all you will do is to " | ||
+ | who has watched an eclipse with a little pinhole camera has seen this. | ||
+ | |||
+ | By the way, if you stare at the above picture for a while, you may realize | ||
+ | that, in that geometric model, the object gets turned upside-down on the | ||
+ | film. | ||
+ | |||
+ | Now that we have a physical model of the equations that have been thrown | ||
+ | around, let's look at what we've been doing. | ||
+ | |||
+ | Consider a cube centered at the origin. | ||
+ | z=0. What if one side of the cube has part of it's face below the x-y plane | ||
+ | (negative z) and part above the x-y plane? | ||
+ | trace rays through the origin, you'll see one part of the face at one end of | ||
+ | the film (negative z, say), and the other part way the heck out at the other | ||
+ | end! And the two parts don't touch, either! | ||
+ | |||
+ | So we need to be careful. | ||
+ | object was a fair distance away from the lens. Currently we have our lens | ||
+ | at the center of our cube, so something needs to move! Since rotations are | ||
+ | defined about the origin we can't just redefine the cube so that the | ||
+ | vertices are out at, say, z=2 and z=3. So what we need to do is to move the | ||
+ | camera away from the cube. Or, if you want to think of it another way, we | ||
+ | need to move the cube away from the camera before we take a picture of it. | ||
+ | |||
+ | In this case the translation needs to be done in the z-direction. The new | ||
+ | projection equations are then | ||
+ | |||
+ | x1 = d*x/ | ||
+ | |||
+ | Where z0 is a translation amount that at the very least makes sure that | ||
+ | z-z0 < 0. | ||
+ | |||
+ | Now not only have we eliminated possible problems with dividing by zero, but | ||
+ | the mathematics now match the physical model. | ||
+ | |||
+ | Some of you might want to think about the less-physical situation of putting | ||
+ | the object _behind_ the film, i.e. to the right of the film in the above | ||
+ | picture. | ||
+ | |||
+ | As usual, there are some deeper mathematics lurking behind these equations, | ||
+ | called projective geometry. | ||
+ | introduction to the subject (at least, I think the book was published; my | ||
+ | copy is an old preprint). | ||
+ | |||
+ | Implementation | ||
+ | -------------- | ||
+ | |||
+ | Now that we've got the theory under our belt, we need to think about | ||
+ | implementing it on the computer. | ||
+ | immediately skipped to this section, most of the discussion will be at a | ||
+ | reasonably high level. | ||
+ | |||
+ | One thing you need to understand is 8-bit signed and unsigned numbers. | ||
+ | is a quick review: an 8-bit unsigned number ranges from 0..255. | ||
+ | signed number ranges from -128..127 and is written in two' | ||
+ | In an 8-bit two' | ||
+ | usually do, but the seventh (high) bit represents the sign of the number in | ||
+ | a special way. To find the 8-bit two' | ||
+ | from 256. Example: what is -21 in two's complement notation? | ||
+ | = 235 = $EB. What is the complement of -21? It is 256-235 = 21 -- like | ||
+ | magic. | ||
+ | $01, 0 is $00, -1 is $FF, -2 is $FE, etc. And what is 24-21 in two's | ||
+ | complement? It is: 24 + -21 = $EE + $EB = $0103. | ||
+ | (subtract 256) and we come out with... $03! | ||
+ | |||
+ | Onwards! | ||
+ | |||
+ | First, we need to decide what language to use. You and I both know the | ||
+ | answer here: BASIC! | ||
+ | Commodore 64 is spelled a-s-s-e-m-b-l-y. | ||
+ | |||
+ | Next, we need to decide what kind of math we want to use, signed or | ||
+ | unsigned. | ||
+ | positive numbers in funny ways, we definitely want to use signed numbers. | ||
+ | The alternative is to have lots of code and overhead to handle all the | ||
+ | cases, and if we put it in two' | ||
+ | the work for us. | ||
+ | |||
+ | How big should the numbers be? Since we are going for speed here, the | ||
+ | obvious choice is 8-bits. | ||
+ | -128..127, is that OK? The size of our grid is 0..127 x 0..127, so this is | ||
+ | perfect! | ||
+ | consider the expression (a+b)/ | ||
+ | numbers within our range of numbers, and the expression evaluates to 64, | ||
+ | which is also in our range, BUT: if you evaluate the above in two's | ||
+ | complement form, you will find different answers depending on how you | ||
+ | evaluate it (i.e. (a+b)/2 will not give the same answer as a/2 + b/2, which | ||
+ | will give the correct answer). | ||
+ | |||
+ | Now we've got another problem: sine and cosine range between negative one | ||
+ | and one. To represent these floating point numbers as 8-bit signed integers | ||
+ | the idea will be to multiply all floating point numbers by a fixed amount. | ||
+ | That is, instead of dealing with the number 0.2, we use the number 64*0.2 = | ||
+ | 12.8 = 13, and divide the end result by 64. As usual, we are trading | ||
+ | accuracy for speed, although it will turn out to make little difference | ||
+ | here. | ||
+ | |||
+ | Why did I pick 64? Obviously we want to pick some factor of two to make the | ||
+ | division at the end simple (just an LSR). 128 is too big. 32 doesn' | ||
+ | us much accuracy. | ||
+ | evaluation (see the above example of (a+b)/2), but as we shall see 64 will | ||
+ | work out nicely. | ||
+ | |||
+ | Now that we have accomplished the difficult task of decision making, we now | ||
+ | need to move on to the simple task of implementation, | ||
+ | rotations. | ||
+ | |||
+ | Implementation: | ||
+ | ------------------------- | ||
+ | |||
+ | We've got some more heavy-duty decision making ahead of us. We could | ||
+ | implement this is several ways. We could apply each rotation individually, | ||
+ | that is, we could rotate around the z-axis, then use these rotated points | ||
+ | and rotate them around the y-axis, etc. | ||
+ | |||
+ | Well, yes, that would work, but... each rotation is nine multiplications. | ||
+ | Each multiplication involves a lot of work, plus we have to shift the result | ||
+ | by our fixed amount each time. We would not only be using huge amounts of | ||
+ | time, but we would lose a lot of accuracy in the process. | ||
+ | speaking, this is called a "bad idea". | ||
+ | |||
+ | Once again, mathematics saves the day: here is where we get the payoff for | ||
+ | writing the equations as an algebraic system (a matrix). | ||
+ | transformation around the x-axis, Y the transformation around the y-axis, | ||
+ | and Z the transformation around the z-axis, then this is the equation to | ||
+ | transform a vector v by rotating the point first around the z-axis, then the | ||
+ | y-axis, then the x-axis: | ||
+ | |||
+ | XYZv = v' | ||
+ | |||
+ | where v' is the new point after all the rotation transformations. (You might | ||
+ | call it a conflagration of rotation transformations). Now the magic of | ||
+ | linear algebra begins to work: operations are associative, | ||
+ | way of saying that (AB)C = A(BC); For us this means that I can multiply all | ||
+ | three matrices X Y and Z together to get a single new matrix M: | ||
+ | |||
+ | M = XYZ | ||
+ | Mv= v' | ||
+ | |||
+ | " | ||
+ | M as we do to apply each rotation separately! | ||
+ | help?" | ||
+ | |||
+ | 1) We now have a single matrix which describes ALL the rotations. | ||
+ | For a single point we haven' | ||
+ | a lot of points (and a cube has eight), transforming every | ||
+ | point is now a single matrix multiplication. | ||
+ | if we have a lot of points to transform we get a HUGE savings | ||
+ | | ||
+ | |||
+ | 2) We can take advantage of trigonometric identities and in so | ||
+ | doing make the computation of M very simple. | ||
+ | |||
+ | Computationally speaking, this is known as a "good idea". | ||
+ | |||
+ | To multiply the three rotation matrices together, we need to take advantage | ||
+ | of a few trigonometric properties. | ||
+ | earlier: | ||
+ | |||
+ | sin(a+b) = sin(a)cos(b) + cos(a)sin(b) | ||
+ | cos(a+b) = cos(a)cos(b) - sin(a)sin(b) | ||
+ | |||
+ | We will also use the fact that cosine is even and sine is odd, that is | ||
+ | |||
+ | cos(-a) = cos(a) | ||
+ | sin(-a) = -sin(a) | ||
+ | |||
+ | Using the above identities it is easy to see that | ||
+ | |||
+ | sin(a)sin(b) = (cos(a-b) - cos(a+b))/2 | ||
+ | cos(a)cos(b) = (cos(a+b) + cos(a-b))/2 | ||
+ | sin(a)cos(b) = (sin(a+b) + sin(a-b))/2 | ||
+ | |||
+ | We are going to rotate first around the z-axis by an amount sz, then the | ||
+ | y-axis by an amount sy, then the x-axis by an amount sx. Why rotate in that | ||
+ | order? | ||
+ | |||
+ | M = XYZ | ||
+ | |||
+ | If you multiply everything out (and I encourage you to do so, not only for | ||
+ | practice, but also as a double-check of my work), and use the above trig | ||
+ | identities, the result is: | ||
+ | |||
+ | [A B C] | ||
+ | M = [D E F] | ||
+ | [G H I] | ||
+ | |||
+ | Where | ||
+ | A = (cos(t1)+cos(t2))/ | ||
+ | B = (sin(t1)-sin(t2))/ | ||
+ | C = sin(sy) | ||
+ | D = (sin(t3)-sin(t4))/ | ||
+ | E = (cos(t3)+cos(t4))/ | ||
+ | F = (sin(t9)-sin(t10))/ | ||
+ | G = (cos(t4)-cos(t3))/ | ||
+ | H = (sin(t3)+sin(t4))/ | ||
+ | I = (cos(t9)+cos(t10))/ | ||
+ | |||
+ | with | ||
+ | t1 = sy-sz | ||
+ | t2 = sy+sz | ||
+ | t3 = sx+sz | ||
+ | t4 = sx-sz | ||
+ | t5 = sx+sy+sz = sx+t2 | ||
+ | t6 = sx-sy+sz = sx-t1 | ||
+ | t7 = sx+sy-sz = sx+t1 | ||
+ | t8 = sy+sz-sx = t2-sx | ||
+ | t9 = sy-sx | ||
+ | t10= sy+sx | ||
+ | |||
+ | How is this supposed to be the " | ||
+ | there are no multiplies. | ||
+ | about the same time as it would take to do two multiplications. This also | ||
+ | means that the associated problem with multiplications, | ||
+ | now gone. | ||
+ | |||
+ | Here is also where we need to be extremely careful. | ||
+ | matrix M is the example I gave earlier about evaluating signed numbers. | ||
+ | do we overcome this? | ||
+ | |||
+ | Easy! Notice in the matrix M that, apart from element C, every term is a | ||
+ | sine or a cosine divided by two. This is the only part of the program which | ||
+ | uses sines and cosines, so why not use the offset floating-point values | ||
+ | divided by two? This will make more sense in a minute. | ||
+ | |||
+ | The question arises: the above is all well and good, but how do we take the | ||
+ | sine of a number and make it fast? The answer of course is to use a table. | ||
+ | We used a BASIC routine to calculate the table for us (and to store the | ||
+ | numbers in two' | ||
+ | angle you want ahead of time, and then just look up the number. | ||
+ | |||
+ | The tables contain the values of sine and cosine multiplied by 64 (our | ||
+ | floating-point offset) and then divided by 2. Since the value is already | ||
+ | divided by two, the above calculation becomes at the same time faster and | ||
+ | safer: faster because I don't have to keep dividing by two, and safer | ||
+ | because I don't have to worry so much about overflow. | ||
+ | but it won't if you're careful). | ||
+ | |||
+ | Here is an example of how to calculate elements A and B above: | ||
+ | |||
+ | LDA sy | ||
+ | SEC | ||
+ | SBC sz | ||
+ | ... | ||
+ | STA t1 ; | ||
+ | LDA sy | ||
+ | CLC | ||
+ | ADC sz | ||
+ | ... | ||
+ | STA t2 ; | ||
+ | ... | ||
+ | LDX t1 | ||
+ | LDA COS,t1 ;COS is a table of cosines*offset/ | ||
+ | LDX t2 | ||
+ | CLC | ||
+ | ADC COS,t2 | ||
+ | STA A ; | ||
+ | LDX t1 | ||
+ | LDA SIN,t1 | ||
+ | LDX t2 | ||
+ | SEC | ||
+ | SBC SIN,t2 | ||
+ | STA B ; | ||
+ | ... ; | ||
+ | |||
+ | Note that the elements D E G and H involve a division by four, which means | ||
+ | that the code does need to perform a division by two during the calculation | ||
+ | of those elements. | ||
+ | |||
+ | That's all there is to calculating the rotation matrix. | ||
+ | actually rotate the points. | ||
+ | the rotated object and rotate it by a little amount, or do we take the | ||
+ | original object and rotate it by a big amount? | ||
+ | set things up, the answer is clear: we want to increment the angle at each | ||
+ | step, and rotate the original object by this large angle (besides, | ||
+ | geometrically you can see that it will look much nicer this way). | ||
+ | |||
+ | For a cube this is easy. The points are 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]. | ||
+ | This means that the rotations are just a series of additions and/or | ||
+ | subtractions of A, | ||
+ | partly to make these procedures easy to see, but mostly to make debugging | ||
+ | the code much easier. | ||
+ | i.e. | ||
+ | |||
+ | :P1 LDA A | ||
+ | ADC B | ||
+ | ADC C | ||
+ | STA P1.X | ||
+ | ... | ||
+ | |||
+ | :P2 LDA A | ||
+ | SBC B | ||
+ | ADC C | ||
+ | STA P2.X | ||
+ | ... | ||
+ | :P3 LDA C | ||
+ | SBC A | ||
+ | SBC B | ||
+ | STA P3.X | ||
+ | |||
+ | You get the idea. Of course, the code needs to remember that it is dealing | ||
+ | with signed numbers, and to watch carry flags carefully (something the above | ||
+ | fragment does not do). | ||
+ | |||
+ | Still worried about overflow? | ||
+ | see that the maximum value any part of a rotated coordinate can have is | ||
+ | sqrt(3). | ||
+ | instance, the maximum possible value for A+B+C is 64*sqrt(3) which is about | ||
+ | 111 -- in range of a signed 8-bit number with a little cushion for | ||
+ | additions. | ||
+ | |||
+ | So far we have managed to rotate all the coordinates -- a complicated series | ||
+ | of matrix operations involving trigonometric functions -- by just using a | ||
+ | bunch of additions and a bunch of table lookups! | ||
+ | need to project the point. | ||
+ | |||
+ | Implementation: | ||
+ | --------------------------- | ||
+ | |||
+ | Recall that the projection equation is | ||
+ | |||
+ | x' = d*x/(z-z0) | ||
+ | y' = d*y/(z-z0) | ||
+ | |||
+ | It looks as if we have gone from a bunch of sneaky additions to | ||
+ | multiplications and divisions! | ||
+ | |||
+ | Well, wait a minute, maybe we can do something. | ||
+ | 1/(z-z0), and then just use a multiply? | ||
+ | number. | ||
+ | Come to think of it, if the number weren' | ||
+ | would be a pretty reasonable number! | ||
+ | |||
+ | So, what we want to do is to construct a table of numbers such that when the | ||
+ | program calls | ||
+ | |||
+ | LDX z | ||
+ | LDA table,z | ||
+ | |||
+ | it gets the absolute (i.e. non-offset) value A=d/ | ||
+ | change d? You could put a little piece of code into your program which | ||
+ | multiplies by a number less than one, and let d represent the maximum value | ||
+ | for d which makes the code work. But for the moment we won't bother with | ||
+ | that -- one thing at a time! | ||
+ | |||
+ | Since z is a signed number, we ought to add 128 to it to convert it into an | ||
+ | index. | ||
+ | also need to remember that floats are offset by 64, and that the highest | ||
+ | value a signed number can have is 127. | ||
+ | |||
+ | Here is how the table is generated: | ||
+ | |||
+ | 10 bz=whatever | ||
+ | 20 d=45: | ||
+ | 30 for i=0 to 255 | ||
+ | 40 q%=64*d/ | ||
+ | 50 poke bz+i, | ||
+ | 60 next | ||
+ | |||
+ | Note that the offset chosen forces q% to always be positive. | ||
+ | be made use of in the multiplication routine (but isn't in the source code). | ||
+ | |||
+ | You may have noticed that z0-z is used, and not z-z0 like in the projection | ||
+ | equation. | ||
+ | realize that the way the projection equations were set up causes the image | ||
+ | to become inverted. | ||
+ | So we just add that step into the table. | ||
+ | |||
+ | But we still need to do a multiplication! | ||
+ | |||
+ | Fast Signed 8-bit Multiply | ||
+ | -------------------------- | ||
+ | |||
+ | A binary number looks like the following: | ||
+ | |||
+ | P = 1*128 + 0*64 + 0*32 + 1*16 + 1*8 + 0*4 + 0*2 + 0*1 | ||
+ | |||
+ | Therefore, if we want to multiply P by another number, 13 say, we find that | ||
+ | |||
+ | 13*P = 13*128 + 0*64 + 0*32 + 13*16 + ... | ||
+ | |||
+ | that is to say, if there is a one in bit position N, then the new number | ||
+ | will have 13*2^N in it. So, to multiply two numbers we find out what bit | ||
+ | positions are high, and then add the other number*2^N to the result. This | ||
+ | doesn' | ||
+ | |||
+ | So, let's say we want to multiply the number P by the number R. If P has a | ||
+ | high bit in position N, we can start out with 256*R, and bit-shift it to the | ||
+ | right 8-N times. | ||
+ | _pipeline_ the process in a way somewhat similar to the way a Cray | ||
+ | supercomputer multiplies two vectors together -- yes, I'm comparing your | ||
+ | C-64 to a Cray! Watch: | ||
+ | |||
+ | * | ||
+ | * 8-bit multiply -> 16-bit result | ||
+ | * | ||
+ | * ACC*AUX -> [AUX, | ||
+ | |||
+ | MULT LDA #$00 | ||
+ | LDY #$09 | ||
+ | ]LOOP LSR | ||
+ | ROR ACC | ||
+ | BCC MULT2 | ||
+ | CLC | ||
+ | ADC AUX | ||
+ | MULT2 DEY | ||
+ | BNE ]LOOP | ||
+ | STA EXT | ||
+ | |||
+ | Pretty slick. | ||
+ | do is check to see if the result is positive or negative. If it's positive, | ||
+ | we check one number (they are either both positive or both negative), and if | ||
+ | it's negative we fix them both to be positive, and use the above process. | ||
+ | If the result is going to be negative, we need to find the negative number, | ||
+ | make it positive, multiply the two numbers together, and make the final | ||
+ | result negative (take the two' | ||
+ | |||
+ | See the source code for an implementation of this. | ||
+ | |||
+ | Note that we could do a divide in a similar fashion, except shifting left | ||
+ | instead of right. | ||
+ | we don't need to worry about this. | ||
+ | |||
+ | Now we have all the tools we need to implement the mathematics. There is | ||
+ | still one part of the program left: drawing the thing! | ||
+ | |||
+ | Drawing a line | ||
+ | -------------- | ||
+ | |||
+ | The geometric idea is: given an initial point [x1 y1], we want to draw a | ||
+ | line to the point [x2 y2]! Now we want to do this on a computer by taking | ||
+ | one step at a time, from point to point. | ||
+ | since we're on a C64 there aren't any MUL or DIV instructions. | ||
+ | |||
+ | To do this, we first need to find out which is larger: | ||
+ | |||
+ | dx = |x2-x1| | ||
+ | dy = |y2-y1| | ||
+ | |||
+ | where | | denotes absolute value. | ||
+ | variable x is going to run from x1 to x2. Therefore, we want to increase x | ||
+ | by one at each step, and we want to increase y by some fractional amount (If | ||
+ | dy were larger we would want to take big steps in the y-direction). But we | ||
+ | don't want to calculate this fractional number. | ||
+ | take a certain amount of steps in the x-direction before taking a step in | ||
+ | the y-direction. | ||
+ | |||
+ | If we take k steps in x before taking a step in y, then we want to chose k | ||
+ | such that | ||
+ | |||
+ | dx/k = dy | ||
+ | |||
+ | which gives | ||
+ | |||
+ | k = dx/dy | ||
+ | |||
+ | where dx and dy are as above, the total number of steps to be taken in the | ||
+ | x- and y-directions respectively. | ||
+ | every time we step in x, we need to increase a counter by the amount dy. As | ||
+ | soon as this counter is larger than dx, we have successfully divided dy into | ||
+ | dx, and so simply reset the counter (in a special way, so that we keep any | ||
+ | remainder from the division) and take a step in y. | ||
+ | |||
+ | Of course, if dy were larger than dx, the idea would be the same, but now k | ||
+ | = dy/ | ||
+ | |||
+ | In the code fragment which follows it is assumed that x2> | ||
+ | dx> | ||
+ | handle all of these cases. | ||
+ | for each case. Another way (the way used by the program), is to force | ||
+ | x2>x1, so that there are only four cases to deal with. For the plotting | ||
+ | routine which we use, this turns out to be necessary. | ||
+ | it, you can come up with some more clever ways to deal with this. | ||
+ | |||
+ | Note that you also need to figure out what column the first point is in: | ||
+ | this algorithm knows how to walk forwards, but it doesn' | ||
+ | should start. | ||
+ | |||
+ | The code is next to some similar BASIC7.0 code to make it easier to | ||
+ | understand. | ||
+ | |||
+ | The code can be sped up in a lot of ways. For one thing it could be made | ||
+ | self-modifying. | ||
+ | entire routine could be stored in zero page! Also, with a little change in | ||
+ | the logic (and a subsequent change in the plotting routine) you can | ||
+ | eliminate the branching instruction. | ||
+ | that here; maybe in another paper ;-). | ||
+ | |||
+ | Also note that the largest value x can take on in this routine is 255. For | ||
+ | the way we are going to plot things, this won't matter. But a more general | ||
+ | routine needs a way to overcome this. One way would be to draw two separate | ||
+ | lines. | ||
+ | |||
+ | 10 REM All of the above comments ;-) | ||
+ | 20 REM Input x1,x2,y1,y2 | ||
+ | 30 GRAPHIC1, | ||
+ | 31 :REM above is a double-check ; | ||
+ | 39 REM Set up variables ; | ||
+ | 40 DX = X2-X1 LDA $(X2) ; | ||
+ | SBC $(X1) | ||
+ | STA DX ; | ||
+ | 50 DY = Y2-Y1 LDA $(Y2) | ||
+ | SBC $(Y1) | ||
+ | STY DY | ||
+ | 60 X=X1: | ||
+ | LDY $(Y1) ; | ||
+ | 64 REM A counts steps in x | ||
+ | 65 REM Below you might want to | ||
+ | 66 REM change to A=1 or A=DY | ||
+ | 67 REM Otherwise the line always | ||
+ | 68 REM takes only one step in y | ||
+ | 69 REM before the last point (x=x2-1) | ||
+ | 70 A=256-DX: | ||
+ | SEC | ||
+ | SBC | ||
+ | 80 DRAW1, | ||
+ | 90 REM Main routine | ||
+ | 100 X=X+1 LOOP INX ;Step in x | ||
+ | 110 A=A+DY | ||
+ | 120 IF A>=256 THEN Y=Y+1: | ||
+ | 121 REM IF A>=DX THEN... INY ; | ||
+ | SBC DX ; | ||
+ | 130 DRAW1, | ||
+ | 140 IF X<>X2 THEN GOTO 100 CPX X2 ;At the endpoint yet? | ||
+ | BNE LOOP | ||
+ | 150 PRINT" | ||
+ | |||
+ | Cycle count: | ||
+ | LOOP: 2 3 2 2 3 3 3 = 18 | ||
+ | | ||
+ | + dx PPLOTs (one for each point) | ||
+ | |||
+ | The point here is that it's fast. If you use self-modifying code, you can | ||
+ | get this down to 15 cycles per point. | ||
+ | down to 13 cycles per point, excluding plot, worst case. Not too bad! We | ||
+ | won't be clever right now, but maybe you'll get to see it later... | ||
+ | |||
+ | Note also that this could easily be used in a BASIC program; even a BASIC2.0 | ||
+ | program. | ||
+ | line, er... contact us). | ||
+ | |||
+ | Now, this routine works fine, but for drawing a line on a computer it | ||
+ | doesn' | ||
+ | from 1,1 to 11,3? k=dx/dy=5, so se will take five steps in x and then a | ||
+ | step in y, then five more steps and... a step in y at the very last point! | ||
+ | So our line doesn' | ||
+ | endpoint. | ||
+ | |||
+ | One way to fix this is to trick the computer into thinking it needs to take | ||
+ | an extra step in y by letting k=dx/ | ||
+ | track of our counter. | ||
+ | the square end-pixels when dx and dy are nearly the same (slope ~= 1). | ||
+ | |||
+ | A better way to fix this is to initialize the counter not to 0 (in our case, | ||
+ | 256-dx), but instead to DX/2 (256-DX/2 in our case). This has the effect of | ||
+ | splitting one of the line segments between the two endpoints, and looks good | ||
+ | for all slopes. | ||
+ | tell, this is what BASIC7.0 does too! | ||
+ | |||
+ | There is still a part of our routine missing, however... | ||
+ | |||
+ | Plotting a point | ||
+ | ---------------- | ||
+ | |||
+ | In the line routine presented earlier, the nebulous statement PPLOT was | ||
+ | written. | ||
+ | |||
+ | For this project, speed is the name of the game, and for speed we don't want | ||
+ | to use normal bitmapped graphics. | ||
+ | graphics. | ||
+ | |||
+ | - Less memory | ||
+ | - Speed of plotting | ||
+ | - Double buffering | ||
+ | - Convenient organization | ||
+ | |||
+ | The first advantage, less memory, should be clear. | ||
+ | takes up 2k. A bitmap, on the other hand, takes up 8k. | ||
+ | |||
+ | For the second advantage, it is much faster to poke a character into screen | ||
+ | memory than it is to calculate and plot all 64 bits in a character. | ||
+ | way, VIC does all the hard work for us. Also, if we are clever, we can | ||
+ | exploit several aspects of our cleverness to make plotting a single point | ||
+ | much easier. | ||
+ | |||
+ | Character graphics also give us a very simple means of double buffering: we | ||
+ | can just plot into two different character sets and tell VIC-II to move | ||
+ | between them. No raster interrupts here! If the two character sets were at | ||
+ | $3000 and $3800, here is how to switch between them: | ||
+ | |||
+ | LDA VMCSB ; | ||
+ | EOR # | ||
+ | STA VMCSB | ||
+ | |||
+ | True, clearing the buffer each time is a bit slow, but for our purposes it | ||
+ | will do just fine. | ||
+ | |||
+ | The last is less obvious. | ||
+ | following: | ||
+ | |||
+ | 00 08 ... | ||
+ | 01 09 | ||
+ | 02 0A | ||
+ | ... ... | ||
+ | 07 0F | ||
+ | |||
+ | where the number represents the offset of the byte. This is fine for some | ||
+ | things, but calculating the position of a pixel is tricky. | ||
+ | map, we can represent our data any way we want. In particular, we can | ||
+ | organize our bitmap to look like the following: | ||
+ | |||
+ | 00 80 ... | ||
+ | 01 81 | ||
+ | 02 82 | ||
+ | ... ... | ||
+ | 7D FD | ||
+ | 7E FE | ||
+ | 7F FF ... | ||
+ | |||
+ | Or, in graphic form | ||
+ | |||
+ | @P... etc. | ||
+ | AQ | ||
+ | BR | ||
+ | CS | ||
+ | .. | ||
+ | O< | ||
+ | |||
+ | What we have done is, instead of putting characters side-by-side like a | ||
+ | hires bitmap does, we put them on top of each other. | ||
+ | 16x16 character array, which is a 128x128 pixel array. Now the y-coordinate | ||
+ | is a direct index into the row we are in. That is, base+Y = memory location | ||
+ | of point. | ||
+ | |||
+ | This brings us to the primary disadvantage of using a character set: our | ||
+ | pictures are pretty small. | ||
+ | |||
+ | Now we could just go merrily plotting into our character bitmap, but as | ||
+ | usual a little thought can yield some impressive return. | ||
+ | notice is that the maximum value for y is 127; the only thing that sets the | ||
+ | high bit is the x-coordinate, | ||
+ | look at the above memory map if you don't see it). | ||
+ | |||
+ | Therefore, if we could keep track of the bit position of x, we could tell | ||
+ | when x crossed a column, and just add 128 to the base address. | ||
+ | that, but we also know to increase the high byte of the pointer by one when | ||
+ | we have crossed two columns. | ||
+ | |||
+ | The logic is as follows: | ||
+ | - Find the bit pattern for a given x (for speed, use a table) | ||
+ | - If it is 10000000 then we have jumped a column | ||
+ | - If the column we are in doesn' | ||
+ | in the low byte of the pointer to the base of the column, | ||
+ | then set the high bit (add 128) | ||
+ | - Otherwise, set the high bit to zero (add 128), and increase | ||
+ | the high byte of the column pointer (step into the next page). | ||
+ | |||
+ | Here is (more or less) the code: | ||
+ | |||
+ | In BASIC: | ||
+ | 2000 rem bp(x) contains bit position for x | ||
+ | 2010 if int(x/8) = x/8 then base=base+128 | ||
+ | 2020 poke base+y, (peek(base+y) or bp(x)) | ||
+ | |||
+ | In assembly: | ||
+ | LDA BITP, | ||
+ | BPL CONT 3 | ||
+ | EOR $LO | ||
+ | STA $LO | ||
+ | BMI CONT | ||
+ | INC $HI | ||
+ | LDA # | ||
+ | CONT ORA ($LO),Y 5 | ||
+ | STA ($LO), | ||
+ | -------- | ||
+ | Cycle count: 18 26 32 | ||
+ | |||
+ | Therefore, it takes 18 cycles to plot a point, 26 cycles to jump a column, | ||
+ | and 32 cycles to jump a page. Over 16 points, this averages 19.375 cycles. | ||
+ | |||
+ | When combined with the earlier line drawing routine, this gives an average | ||
+ | time of 38 cycles or so (with a best time of 34 cycles); six of those cycles | ||
+ | are for PHA and PLA, since the line drawing routine uses A for other things. | ||
+ | |||
+ | Like most of the code, you can improve on this method if you think about it | ||
+ | a little. | ||
+ | you avoid them? Maybe if we do another article, we'll show you our | ||
+ | solution(s). | ||
+ | |||
+ | Now, this method has a few subtleties about it. First, what happens if the | ||
+ | first point to be plotted is x=0, or x=8? The above routine will increase | ||
+ | the base pointer right off the bat. This case needs to be taken care of. | ||
+ | |||
+ | Second, the above assumes that you always take a step in x. What happens if | ||
+ | we are taking a big step in y? Let's say that we take ten steps in y for | ||
+ | every step in x. What will the above plotter do if x takes a step across a | ||
+ | column, and then doesn' | ||
+ | solution to this problem. | ||
+ | |||
+ | So that's all there is to it! | ||
+ | |||
+ | Post Script | ||
+ | ----------- | ||
+ | |||
+ | That's all there is to it. Well, OK, there are a few details we left out, | ||
+ | but you can figure them out on your own. You can always look to the source | ||
+ | code to see how we overcame the same problem. | ||
+ | way that you can experiment around with the projection parameters d and z0, | ||
+ | to see what changing them does to the math. | ||
+ | |||
+ | What's next? In the future you will undoubtably see lots of things from | ||
+ | George and myself, both the written word and the coded byte. Maybe we will | ||
+ | see something from you as well? | ||
+ | |||
+ | Da Code | ||
+ | ------- | ||
+ | |||
+ | ******************************** | ||
+ | *..............................* | ||
+ | *.Stephen.Judd.................* | ||
+ | *.George.Taylor................* | ||
+ | *.Started: | ||
+ | *.Finished: | ||
+ | *..............................* | ||
+ | *.Well, | ||
+ | *.program.will.rotate.a.cube...* | ||
+ | *..............................* | ||
+ | *.This.program.is.intended.to..* | ||
+ | *.accompany.the.article.in.....* | ||
+ | *.C=Hacking, | ||
+ | *.For.details.on.this.program, | ||
+ | *.read.the.article!............* | ||
+ | *..............................* | ||
+ | *.Write.to.us!.................* | ||
+ | *..............................* | ||
+ | *.un(bee)mo....................* | ||
+ | *..............................* | ||
+ | *.vi...........................* | ||
+ | *.n(in)g.......................* | ||
+ | *.are(th.......................* | ||
+ | *.e)you(o......................* | ||
+ | *.nly).........................* | ||
+ | *..............................* | ||
+ | *.asl(rose)eep.................* | ||
+ | *..............e.e.cummings....* | ||
+ | *..............................* | ||
+ | *.P.S..This.was.written.using..* | ||
+ | *......Merlin.128...With.a.....* | ||
+ | *......little.modification.it..* | ||
+ | *......will.work.fine.with.....* | ||
+ | *......Merlin.64...If.you......* | ||
+ | *......don' | ||
+ | *......well, | ||
+ | *......little.faults...........* | ||
+ | ******************************** | ||
+ | |||
+ | ORG $1000 | ||
+ | |||
+ | *.Constants | ||
+ | |||
+ | BUFF1 EQU $3000 ; | ||
+ | BUFF2 EQU $3800 ; | ||
+ | BUFFER EQU $A3 ; | ||
+ | X1 EQU $FB ; | ||
+ | Y1 EQU $FC ; | ||
+ | X2 EQU $FD ; | ||
+ | Y2 EQU $FE | ||
+ | DX EQU $F9 | ||
+ | DY EQU $FA | ||
+ | TEMP1 EQU $FB ; | ||
+ | TEMP2 EQU $FC ; | ||
+ | ACC EQU $FB ; | ||
+ | AUX EQU $FC ; | ||
+ | EXT EQU $FD | ||
+ | REM EQU $FE | ||
+ | ZTEMP EQU $02 ; | ||
+ | |||
+ | ANGMAX EQU 120 ; | ||
+ | OFFSET EQU 6 ; | ||
+ | |||
+ | *.VIC | ||
+ | |||
+ | VMCSB EQU $D018 | ||
+ | BKGND EQU $D020 | ||
+ | BORDER EQU $D021 | ||
+ | SSTART EQU 1344 ; | ||
+ | |||
+ | |||
+ | *.Kernal | ||
+ | |||
+ | CHROUT EQU $FFD2 | ||
+ | GETIN EQU $FFE4 | ||
+ | |||
+ | ***.Macros | ||
+ | |||
+ | MOVE MAC | ||
+ | LDA ]1 | ||
+ | STA ]2 | ||
+ | <<< | ||
+ | |||
+ | GETKEY MAC ; | ||
+ | WAIT JSR GETIN | ||
+ | CMP #00 | ||
+ | BEQ WAIT | ||
+ | <<< | ||
+ | |||
+ | DEBUG MAC ; | ||
+ | . DO.0 ; | ||
+ | |||
+ | LDA #]1 | ||
+ | JSR CHROUT | ||
+ | >>> | ||
+ | CMP #' | ||
+ | BNE L1 | ||
+ | JSR CLEANUP | ||
+ | JMP DONE | ||
+ | L1 CMP #' | ||
+ | BNE DONE | ||
+ | JMP CLEANUP | ||
+ | FIN | ||
+ | DONE <<< | ||
+ | |||
+ | DEBUGA MAC | ||
+ | DO.0 | ||
+ | LDA ]1 | ||
+ | STA 1024 | ||
+ | FIN | ||
+ | DONEA <<< | ||
+ | |||
+ | SETBUF MAC ; | ||
+ | LDA #00 | ||
+ | STA BUFFER | ||
+ | LDA ZTEMP ; | ||
+ | STA BUFFER+1 | ||
+ | <<< | ||
+ | |||
+ | *------------------------------- | ||
+ | |||
+ | LDA #$00 | ||
+ | STA BKGND | ||
+ | STA BORDER | ||
+ | LDA VMCSB | ||
+ | AND #%00001111 ; | ||
+ | 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 ' | ||
+ | HEX 0D1D1D9E12 | ||
+ | TXT ' | ||
+ | TXT ' | ||
+ | HEX 1D1D12 | ||
+ | TXT ' | ||
+ | TXT ' | ||
+ | HEX 1D1D12 | ||
+ | TXT ' | ||
+ | TXT ' | ||
+ | HEX 1D1D12 | ||
+ | TXT ' | ||
+ | TXT ' | ||
+ | TXT ' | ||
+ | HEX 0D05 | ||
+ | TXT ' | ||
+ | HEX 00 | ||
+ | TITLE LDA (TEMP1),Y | ||
+ | BEQ :CONT | ||
+ | JSR CHROUT | ||
+ | INY | ||
+ | BNE TITLE | ||
+ | INC TEMP2 | ||
+ | JMP TITLE | ||
+ | :CONT >>> | ||
+ | |||
+ | ****.Set.up.tables(? | ||
+ | |||
+ | *.Tables.are.currently.set.up.in.BASIC | ||
+ | *.and.by.the.assembler. | ||
+ | |||
+ | TABLES | ||
+ | |||
+ | ****.Clear.screen.and.set.up." | ||
+ | |||
+ | SETUP LDA #147 | ||
+ | JSR CHROUT | ||
+ | LDA #<SSTART | ||
+ | ADC #12 ; | ||
+ | STA TEMP1 ;Column.12 | ||
+ | LDA #>SSTART ;Row.9 | ||
+ | STA TEMP1+1 ; | ||
+ | LDA #00 | ||
+ | LDY #00 | ||
+ | LDX #00 ; | ||
+ | CLC | ||
+ | |||
+ | :LOOP STA (TEMP1),Y | ||
+ | INY | ||
+ | ADC #16 | ||
+ | BCC :LOOP | ||
+ | CLC | ||
+ | LDA TEMP1 | ||
+ | ADC #40 ; | ||
+ | STA TEMP1 ; | ||
+ | LDA TEMP1+1 | ||
+ | ADC #00 ; | ||
+ | STA TEMP1+1 | ||
+ | LDY #00 | ||
+ | INX | ||
+ | | ||
+ | CPX #16 | ||
+ | BNE :LOOP ; | ||
+ | |||
+ | >>> | ||
+ | ****.Set.up.buffers | ||
+ | |||
+ | LDA #<BUFF1 | ||
+ | STA BUFFER | ||
+ | LDA #>BUFF1 | ||
+ | STA BUFFER+1 | ||
+ | STA ZTEMP ; | ||
+ | LDA VMCSB | ||
+ | AND #%11110001 ; | ||
+ | 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 | ||
+ | KPRESS JSR GETIN | ||
+ | CMP #133 ;F1? | ||
+ | BNE :F2 | ||
+ | LDA DSX | ||
+ | CMP #ANGMAX/2 ; | ||
+ | BEQ :CONT | ||
+ | INC DSX ; | ||
+ | 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 ; | ||
+ | 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 | ||
+ | *.>>> | ||
+ | |||
+ | ****.Update.angles | ||
+ | |||
+ | UPDATE CLC | ||
+ | LDA SX | ||
+ | ADC DSX | ||
+ | CMP #ANGMAX ; | ||
+ | BCC :CONT1 | ||
+ | SBC #ANGMAX :If so, reset | ||
+ | :CONT1 STA SX | ||
+ | CLC | ||
+ | LDA SY | ||
+ | ADC DSY | ||
+ | CMP #ANGMAX | ||
+ | BCC :CONT2 | ||
+ | SBC #ANGMAX ;Same.deal | ||
+ | :CONT2 STA SY | ||
+ | CLC | ||
+ | LDA SZ | ||
+ | ADC DSZ | ||
+ | CMP #ANGMAX | ||
+ | BCC :CONT3 | ||
+ | SBC #ANGMAX | ||
+ | :CONT3 STA SZ | ||
+ | |||
+ | |||
+ | ****.Rotate.coordinates | ||
+ | |||
+ | ROTATE | ||
+ | |||
+ | ***.First, | ||
+ | |||
+ | **.Two.macros.to.simplify.our.life | ||
+ | ADDA MAC ; | ||
+ | CLC | ||
+ | LDA ]1 | ||
+ | ADC ]2 | ||
+ | CMP #ANGMAX ; | ||
+ | BCC DONE | ||
+ | SBC #ANGMAX ; | ||
+ | DONE <<< | ||
+ | |||
+ | SUBA MAC ; | ||
+ | SEC | ||
+ | LDA ]1 | ||
+ | SBC ]2 | ||
+ | BCS DONE | ||
+ | ADC #ANGMAX ; | ||
+ | DONE <<< | ||
+ | |||
+ | **.Now.calculate.t1, | ||
+ | |||
+ | >>> | ||
+ | STA T1 ;t1=sy-sz | ||
+ | >>> | ||
+ | STA T2 ;t2=sy+sz | ||
+ | >>> | ||
+ | STA T3 ;t3=sx+sz | ||
+ | >>> | ||
+ | STA T4 ;t4=sx-sz | ||
+ | >>> | ||
+ | STA T5 ;t5=sx+t2 | ||
+ | >>> | ||
+ | STA T6 ;t6=sx-t1 | ||
+ | >>> | ||
+ | STA T7 ;t7=sx+t1 | ||
+ | >>> | ||
+ | STA T8 ;t8=t2-sx | ||
+ | >>> | ||
+ | STA T9 ;t9=sy-sx | ||
+ | >>> | ||
+ | STA T10 ;t10=sx+sy | ||
+ | |||
+ | *.Et.voila! | ||
+ | |||
+ | ***.Next, | ||
+ | |||
+ | **.Another.useful.little.macro | ||
+ | DIV2 MAC ; | ||
+ | ; | ||
+ | BPL POS ; | ||
+ | CLC | ||
+ | EOR #$FF ; | ||
+ | ADC #01 ; | ||
+ | | ||
+ | CLC | ||
+ | EOR #$FF | ||
+ | ADC #01 ; | ||
+ | JMP DONEDIV | ||
+ | POS LSR ; | ||
+ | DONEDIV <<< | ||
+ | |||
+ | MUL2 MAC ; | ||
+ | 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. | ||
+ | |||
+ | :CALCA CLC | ||
+ | LDX T1 | ||
+ | LDA COS,X | ||
+ | LDX T2 | ||
+ | ADC COS,X | ||
+ | STA A11 ; | ||
+ | :CALCB LDX T1 | ||
+ | LDA SIN,X | ||
+ | SEC | ||
+ | LDX T2 | ||
+ | SBC SIN,X | ||
+ | STA B12 ; | ||
+ | :CALCC LDX SY | ||
+ | LDA SIN,X | ||
+ | >>> | ||
+ | STA C13 ;C=sin(sy) | ||
+ | :CALCD SEC | ||
+ | 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 ; | ||
+ | :CALCE SEC | ||
+ | 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 ; | ||
+ | :CALCF LDX T9 | ||
+ | LDA SIN,X | ||
+ | SEC | ||
+ | LDX T10 | ||
+ | SBC SIN,X | ||
+ | STA F23 ; | ||
+ | :CALCG LDX T6 | ||
+ | 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 ; | ||
+ | >>> | ||
+ | >>> | ||
+ | :CALCH CLC | ||
+ | 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' | ||
+ | |||
+ | **.Rotate, | ||
+ | DOWNHILL LDA A11 ; | ||
+ | STA TA | ||
+ | LDA B12 ; | ||
+ | STA TB ; | ||
+ | LDA C13 ; | ||
+ | STA TC | ||
+ | LDA D21 ; | ||
+ | STA TD ; | ||
+ | LDA E22 | ||
+ | STA TE ; | ||
+ | LDA F23 ; | ||
+ | STA TF | ||
+ | LDA G31 ; | ||
+ | STA TG | ||
+ | LDA H32 | ||
+ | STA TH | ||
+ | LDA I33 | ||
+ | STA TI | ||
+ | |||
+ | *.A.neat.macro | ||
+ | NEG MAC ; | ||
+ | CLC | ||
+ | LDA ]1 ;number. | ||
+ | EOR #$FF | ||
+ | ADC #$01 | ||
+ | <<< | ||
+ | |||
+ | *.P1=[1.1.1] | ||
+ | JSR PROJECT ; | ||
+ | LDX TX1 ; | ||
+ | LDY TY1 ; | ||
+ | STX P1X ; | ||
+ | STY P1Y | ||
+ | *.P2=[1.-1.1] | ||
+ | >>> | ||
+ | STA TB | ||
+ | >>> | ||
+ | STA TE | ||
+ | >>> | ||
+ | STA TH | ||
+ | JSR PROJECT | ||
+ | LDX TX1 | ||
+ | LDY TY1 | ||
+ | STX P2X | ||
+ | STY P2Y | ||
+ | *.P3=[-1.-1.1] | ||
+ | >>> | ||
+ | STA TA | ||
+ | >>> | ||
+ | STA TD | ||
+ | >>> | ||
+ | STA TG | ||
+ | JSR PROJECT | ||
+ | LDX TX1 | ||
+ | LDY TY1 | ||
+ | STX P3X | ||
+ | STY P3Y | ||
+ | *.P4=[-1.1.1] | ||
+ | LDA B12 | ||
+ | STA TB | ||
+ | LDA E22 | ||
+ | STA TE | ||
+ | LDA H32 | ||
+ | STA TH | ||
+ | JSR PROJECT | ||
+ | LDX TX1 | ||
+ | LDY TY1 | ||
+ | STX P4X | ||
+ | STY P4Y | ||
+ | *.P8=[-1.1.-1] | ||
+ | >>> | ||
+ | STA TC | ||
+ | >>> | ||
+ | STA TF | ||
+ | >>> | ||
+ | STA TI | ||
+ | JSR PROJECT | ||
+ | LDX TX1 | ||
+ | LDY TY1 | ||
+ | STX P8X | ||
+ | STY P8Y | ||
+ | *.P7=[-1.-1.-1] | ||
+ | >>> | ||
+ | STA TB | ||
+ | >>> | ||
+ | STA TE | ||
+ | >>> | ||
+ | STA TH | ||
+ | JSR PROJECT | ||
+ | LDX TX1 | ||
+ | LDY TY1 | ||
+ | STX P7X | ||
+ | STY P7Y | ||
+ | *.P6=[1.-1.-1] | ||
+ | LDA A11 | ||
+ | STA TA | ||
+ | LDA D21 | ||
+ | STA TD | ||
+ | LDA G31 | ||
+ | STA TG | ||
+ | JSR PROJECT | ||
+ | LDX TX1 | ||
+ | LDY TY1 | ||
+ | STX P6X | ||
+ | STY P6Y | ||
+ | *.P5=[1.1.-1] | ||
+ | LDA B12 | ||
+ | STA TB | ||
+ | LDA E22 | ||
+ | STA TE | ||
+ | LDA H32 | ||
+ | STA TH | ||
+ | JSR PROJECT | ||
+ | LDX TX1 | ||
+ | LDY TY1 | ||
+ | STX P5X | ||
+ | STY P5Y | ||
+ | |||
+ | ****.Clear.buffer | ||
+ | |||
+ | >>> | ||
+ | CLRBUF LDA #$00 ; | ||
+ | LDX #$08 ;I.think | ||
+ | LDY #$00 | ||
+ | :LOOP STA (BUFFER),Y | ||
+ | INY | ||
+ | BNE :LOOP | ||
+ | INC BUFFER+1 | ||
+ | DEX | ||
+ | BNE :LOOP | ||
+ | LDA BUFFER+1 | ||
+ | |||
+ | ****.Finally, | ||
+ | |||
+ | LDA P1X ;[1.1.1] | ||
+ | STA TX1 | ||
+ | LDA P1Y | ||
+ | STA TY1 | ||
+ | LDA P2X ;[1.-1.1] | ||
+ | STA TX2 | ||
+ | LDA P2Y | ||
+ | STA TY2 | ||
+ | JSR DRAW ;First.line | ||
+ | |||
+ | LDA P3X ;[-1.-1.1] | ||
+ | STA TX1 | ||
+ | LDA P3Y | ||
+ | STA TY1 | ||
+ | JSR DRAW ; | ||
+ | |||
+ | LDA P4X ;[-1.1.1] | ||
+ | STA TX2 | ||
+ | LDA P4Y | ||
+ | STA TY2 | ||
+ | JSR DRAW ;Third.line | ||
+ | |||
+ | LDA P1X ;[1.1.1] | ||
+ | STA TX1 | ||
+ | LDA P1Y | ||
+ | STA TY1 | ||
+ | JSR DRAW ; | ||
+ | |||
+ | LDA P5X ;[1.1.-1] | ||
+ | STA TX2 | ||
+ | LDA P5Y | ||
+ | STA TY2 | ||
+ | JSR DRAW ;Five | ||
+ | |||
+ | LDA P6X ;[1.-1.-1] | ||
+ | STA TX1 | ||
+ | LDA P6Y | ||
+ | STA TY1 | ||
+ | JSR DRAW ;Six | ||
+ | |||
+ | LDA P2X ;[1.-1.1] | ||
+ | STA TX2 | ||
+ | LDA P2Y | ||
+ | STA TY2 | ||
+ | JSR DRAW ;Seven | ||
+ | |||
+ | LDA P7X ;[-1.-1.-1] | ||
+ | STA TX2 | ||
+ | LDA P7Y | ||
+ | STA TY2 | ||
+ | JSR DRAW ;Eight | ||
+ | |||
+ | LDA P3X ;[-1.-1.1] | ||
+ | STA TX1 | ||
+ | LDA P3Y | ||
+ | STA TY1 | ||
+ | JSR DRAW ;Nine | ||
+ | |||
+ | LDA P8X ;[-1.1.-1] | ||
+ | STA TX1 | ||
+ | LDA P8Y | ||
+ | STA TY1 | ||
+ | JSR DRAW ;Ten | ||
+ | |||
+ | LDA P4X ;[-1.1.1] | ||
+ | STA TX2 | ||
+ | LDA P4Y | ||
+ | STA TY2 | ||
+ | JSR DRAW ;Eleven | ||
+ | |||
+ | LDA P5X ;[1.1.-1] | ||
+ | STA TX2 | ||
+ | LDA P5Y | ||
+ | STA TY2 | ||
+ | JSR DRAW ;Twelve! | ||
+ | |||
+ | ****.Swap.buffers | ||
+ | |||
+ | SWAPBUF LDA VMCSB | ||
+ | EOR #$02 ; | ||
+ | STA VMCSB | ||
+ | LDA #$08 | ||
+ | EOR ZTEMP ; | ||
+ | STA ZTEMP ; | ||
+ | |||
+ | JMP MAIN ; | ||
+ | |||
+ | |||
+ | *------------------------------- | ||
+ | *.This.subroutine.calculates.the.projection.of.X.and.Y | ||
+ | |||
+ | PROJECT CLC | ||
+ | LDA TG | ||
+ | ADC TH | ||
+ | CLC | ||
+ | ADC TI ; | ||
+ | CLC | ||
+ | ADC #128 ; | ||
+ | | ||
+ | LDA ZDIV,X ; | ||
+ | STA AUX ; | ||
+ | STA REM ; | ||
+ | |||
+ | CLC | ||
+ | LDA TA | ||
+ | ADC TB | ||
+ | CLC | ||
+ | ADC TC | ||
+ | STA ACC ; | ||
+ | JSR SMULT ; | ||
+ | CLC | ||
+ | LDA ACC | ||
+ | :CONT1 ADC #64 ; | ||
+ | *.See.below.for.the.reason.why.this | ||
+ | *.next.instruction.is.commented.out | ||
+ | *.TAX..; | ||
+ | STA TX1 | ||
+ | | ||
+ | LDA REM | ||
+ | STA AUX | ||
+ | LDA TD | ||
+ | ADC TE | ||
+ | CLC | ||
+ | ADC TF | ||
+ | STA ACC ; | ||
+ | JSR SMULT ; | ||
+ | CLC | ||
+ | LDA ACC | ||
+ | :CONT2 ADC #64 ; | ||
+ | *.For.some.completely.unknown.reason.to.me | ||
+ | *.the.instruction.below.doesn' | ||
+ | *.the.RTS.is.modifying.X.and.Y??? | ||
+ | *.TAY..; | ||
+ | STA TY1 | ||
+ | | ||
+ | |||
+ | *------------------------------- | ||
+ | *.SMULT: | ||
+ | * | ||
+ | *.ACC*AUX/ | ||
+ | * | ||
+ | *.Note.that.this.routine.divides.the.end.result.by.2^OFFSET | ||
+ | |||
+ | *.Yup, | ||
+ | DIVOFF MAC ; | ||
+ | LUP OFFSET ; | ||
+ | | ||
+ | ROR ACC ; | ||
+ | --^ | ||
+ | <<< | ||
+ | |||
+ | |||
+ | SMULT CLC | ||
+ | LDA ACC ; | ||
+ | EOR AUX | ||
+ | BMI :NEG | ||
+ | |||
+ | LDA ACC ; | ||
+ | BPL :CONT1 ; | ||
+ | EOR #$FF ; | ||
+ | ADC #$01 ; | ||
+ | STA ACC | ||
+ | >>> | ||
+ | :CONT1 LDA #00 ; | ||
+ | LDY #$09 | ||
+ | ]LOOP LSR ; | ||
+ | ROR ACC | ||
+ | BCC :MULT1 ; | ||
+ | CLC | ||
+ | ADC AUX | ||
+ | :MULT1 DEY | ||
+ | BNE ]LOOP | ||
+ | >>> | ||
+ | STA EXT | ||
+ | RTS | ||
+ | |||
+ | :NEG LDA ACC ; | ||
+ | BMI :CONT2 | ||
+ | >>> | ||
+ | JMP :CONT3 | ||
+ | :CONT2 EOR #$FF ; | ||
+ | ADC #$01 | ||
+ | STA ACC | ||
+ | :CONT3 LDA #00 ;Multiply | ||
+ | LDY #$09 | ||
+ | ]LOOP2 LSR | ||
+ | ROR ACC | ||
+ | BCC :MULT2 | ||
+ | CLC | ||
+ | ADC AUX | ||
+ | :MULT2 DEY | ||
+ | BNE ]LOOP2 | ||
+ | >>> | ||
+ | STA EXT | ||
+ | LDA ACC | ||
+ | BPL :OK ; | ||
+ | JSR CHOKE | ||
+ | :OK EOR #$FF ; | ||
+ | ADC #$01 ; | ||
+ | STA ACC | ||
+ | | ||
+ | |||
+ | *------------------------------- | ||
+ | *.General.questionable-value.error.procedure | ||
+ | |||
+ | CHOKE LDX #00 | ||
+ | :LOOP LDA :CTEXT,X | ||
+ | BEQ :DONE | ||
+ | JSR CHROUT | ||
+ | INX | ||
+ | JMP :LOOP | ||
+ | :DONE RTS | ||
+ | :CTEXT HEX 0D ;CR | ||
+ | TXT ' | ||
+ | HEX 0D00 | ||
+ | |||
+ | *------------------------------- | ||
+ | *.Drawin' | ||
+ | |||
+ | ***.Some.useful.macros | ||
+ | |||
+ | PLOTPX MAC ; | ||
+ | | ||
+ | LDA BITP,X ; | ||
+ | BPL C1 | ||
+ | EOR BUFFER | ||
+ | STA BUFFER | ||
+ | BMI C2 | ||
+ | INC BUFFER+1 | ||
+ | C2 LDA #%10000000 | ||
+ | C1 ORA (BUFFER),Y | ||
+ | STA (BUFFER),Y | ||
+ | | ||
+ | <<< | ||
+ | |||
+ | PLOTPY MAC ; | ||
+ | | ||
+ | LDA BITP,X ; | ||
+ | ORA (BUFFER),Y | ||
+ | STA (BUFFER),Y | ||
+ | PLA | ||
+ | <<< | ||
+ | |||
+ | CINIT MAC ; | ||
+ | LDA ]1 ;dx.or.dy | ||
+ | LSR | ||
+ | EOR #$FF ; | ||
+ | ADC #$01 ; | ||
+ | <<< | ||
+ | |||
+ | XSTEP MAC ; | ||
+ | XLOOP INX | ||
+ | ADC DY | ||
+ | BCC L1 | ||
+ | *.Do.we.use.INY.or.DEY.here? | ||
+ | IF I,]1 ; | ||
+ | INY | ||
+ | ELSE | ||
+ | DEY | ||
+ | FIN | ||
+ | SBC DX | ||
+ | L1 >>> | ||
+ | CPX X2 | ||
+ | BNE XLOOP | ||
+ | <<< | ||
+ | |||
+ | YSTEP MAC ; | ||
+ | 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 ; | ||
+ | LDY Y1 | ||
+ | STA Y1 | ||
+ | STY Y2 | ||
+ | LDA X1 | ||
+ | LDY X2 | ||
+ | STY X1 | ||
+ | STA X2 | ||
+ | |||
+ | SBC X1 ;Now.A=dx | ||
+ | :CONT STA DX | ||
+ | LDX X1 ; | ||
+ | |||
+ | COLUMN LDA X1 ; | ||
+ | | ||
+ | | ||
+ | | ||
+ | LSR | ||
+ | BCC :EVEN ; | ||
+ | LDY #$80 ; | ||
+ | STY BUFFER | ||
+ | CLC | ||
+ | :EVEN ADC BUFFER+1 ; | ||
+ | STA BUFFER+1 ; | ||
+ | |||
+ | SEC | ||
+ | LDA Y2 ; | ||
+ | SBC Y1 | ||
+ | BCS :CONT2 ; | ||
+ | LDA Y1 ; | ||
+ | SBC Y2 | ||
+ | :CONT2 STA DY | ||
+ | CMP DX ; | ||
+ | BCS STEPINY ; | ||
+ | |||
+ | STEPINX LDY Y1 ; | ||
+ | LDA BITP,X ; | ||
+ | ORA (BUFFER),Y | ||
+ | STA (BUFFER),Y | ||
+ | >>> | ||
+ | CPY Y2 | ||
+ | BCS XDECY ; | ||
+ | |||
+ | XINCY >>> | ||
+ | RTS | ||
+ | |||
+ | XDECY >>> | ||
+ | RTS | ||
+ | |||
+ | STEPINY LDY Y1 ; | ||
+ | LDA BITP,X | ||
+ | ORA (BUFFER),Y | ||
+ | STA (BUFFER),Y | ||
+ | >>> | ||
+ | CPY Y2 | ||
+ | BCS YDECY | ||
+ | |||
+ | YINCY >>> | ||
+ | RTS | ||
+ | |||
+ | YDECY >>> | ||
+ | RTS | ||
+ | |||
+ | |||
+ | *------------------------------- | ||
+ | *.Clean.up | ||
+ | |||
+ | CLEANUP LDA VMCSB ; | ||
+ | AND #%11110101 ;default | ||
+ | STA VMCSB | ||
+ | |||
+ | | ||
+ | |||
+ | *------------------------------- | ||
+ | *.Some.variables | ||
+ | |||
+ | TX1 DS 1 | ||
+ | TY1 DS 1 | ||
+ | TX2 DS 1 | ||
+ | TY2 DS 1 | ||
+ | P1X DS 1 ; | ||
+ | P1Y DS 1 ; | ||
+ | P2X DS 1 | ||
+ | P2Y DS 1 ; | ||
+ | P3X DS 1 ; | ||
+ | P3Y DS 1 | ||
+ | P4X DS 1 ; | ||
+ | P4Y DS 1 | ||
+ | P5X DS 1 ; | ||
+ | P5Y DS 1 ; | ||
+ | P6X DS 1 | ||
+ | P6Y DS 1 ; | ||
+ | P7X DS 1 | ||
+ | P7Y DS 1 | ||
+ | P8X DS 1 | ||
+ | P8Y DS 1 | ||
+ | DSX DS 1 ; | ||
+ | DSY DS 1 ; | ||
+ | DSZ DS 1 | ||
+ | SX DS 1 ; | ||
+ | SY DS 1 | ||
+ | SZ DS 1 | ||
+ | T1 DS 1 ; | ||
+ | T2 DS 1 | ||
+ | T3 DS 1 ; | ||
+ | T4 DS 1 | ||
+ | T5 DS 1 | ||
+ | T6 DS 1 | ||
+ | T7 DS 1 | ||
+ | T8 DS 1 | ||
+ | T9 DS 1 | ||
+ | T10 DS 1 | ||
+ | A11 DS 1 ; | ||
+ | B12 DS 1 ;XYZ | ||
+ | C13 DS 1 | ||
+ | D21 DS 1 ; | ||
+ | E22 DS 1 | ||
+ | F23 DS 1 | ||
+ | G31 DS 1 | ||
+ | H32 DS 1 | ||
+ | I33 DS 1 | ||
+ | TA DS 1 ; | ||
+ | TB DS 1 ; | ||
+ | TC DS 1 | ||
+ | TD DS 1 | ||
+ | TE DS 1 | ||
+ | TF DS 1 | ||
+ | TG DS 1 | ||
+ | TH DS 1 | ||
+ | TI DS 1 | ||
+ | |||
+ | *------------------------------- | ||
+ | *.Set.up.bit.table | ||
+ | |||
+ | DS ^ ; | ||
+ | ; | ||
+ | BITP LUP 16 ; | ||
+ | DFB %10000000 | ||
+ | DFB %01000000 | ||
+ | DFB %00100000 | ||
+ | DFB %00010000 | ||
+ | DFB %00001000 | ||
+ | DFB %00000100 | ||
+ | DFB %00000010 | ||
+ | DFB %00000001 | ||
+ | --^ | ||
+ | SIN ; | ||
+ | COS EQU SIN+128 ; | ||
+ | ; | ||
+ | ; | ||
+ | ZDIV EQU COS+128 ; | ||
+ | |||
+ | UUencoded Binaries | ||
+ | ------------------ | ||
+ | begin 666 runme3d | ||
+ | M 0@>" | ||
+ | M. !H:& | ||
+ | F& | ||
+ | |||
+ | end | ||
+ | |||
+ | begin 666 init3d | ||
+ | M 0@D" H CR!04D]' | ||
+ | M15!(14X@2E5$1" | ||
+ | M' | ||
+ | M (%)LC" | ||
+ | MO0@S )DB+B([ -0(-P" | ||
+ | M($, | ||
+ | M.# Z6C" | ||
+ | M4 !1); | ||
+ | M (L@426SJS$R-R" | ||
+ | M"54 ET):JDHL424 K0E: %JR6JI$6CJ" | ||
+ | M2#\B & | ||
+ | 1& | ||
+ | |||
+ | end | ||
+ | |||
+ | begin 666 listme3d | ||
+ | M 0@8" H CR!.3U1%4R!& | ||
+ | M12!35$E, | ||
+ | M2D522UD@1D]2(%--04Q, | ||
+ | M54Y$3T9& | ||
+ | M4DU3+@#>" | ||
+ | M$@"/ | ||
+ | M($U!2T4@6BU:," | ||
+ | M4D535$%25$E.1R$ ;0D5 (\@248@64]5(%=!3E0@5$\@4T5%(%E/ | ||
+ | M4%5415( D@D6 (\@1$E%($%.($%-05I)3D< | ||
+ | M%P"/ | ||
+ | M1U)!32X S0D9 (\ \PD: (\@4T]-151)3453(%1(12!%6453($=%5" | ||
+ | M55-%1 | ||
+ | M (\@0U5" | ||
+ | M24Y+($]2(%1262!43R!& | ||
+ | M12X@($5615(@4T5%3@" | ||
+ | M241%02X N@H@ (\@4TQ*(#< | ||
+ | M& | ||
+ | #&AH: | ||
+ | |||
+ | end | ||
+ | |||
+ | begin 666 cube3d.o | ||
+ | M !"I (T@T(TAT*T8T" | ||
+ | M(" @(" @(" | ||
+ | M2$5.($I51$29(" | ||
+ | M2E5,62 Y-" | ||
+ | M3%, | ||
+ | MDB M($E.0R]$14, | ||
+ | M4D]4051)3TX-' | ||
+ | M(" @(%!215-3($%.62!+15D@5$\@0D5' | ||
+ | M_\D \/FIDR#2_ZE : | ||
+ | MA?R@ .B*X!#0Y*D A: | ||
+ | MTQB-U!@@Y/ | ||
+ | MT!C)//!#[M 83# | ||
+ | MB] +K=$8\!3.T1A, | ||
+ | MK=,8;= 8R7B0 NEXC=, | ||
+ | M& | ||
+ | M& | ||
+ | MVQ@XK=88[=(8L )I> | ||
+ | M& | ||
+ | M 0H82?]I 4PS$PJ-X1@XKMP8O0 :KML8_0 : | ||
+ | M_VD!2AA)_VD!3& | ||
+ | MVQC]@!DXKMP8_8 9$ X82?]I 4H82?]I 4R? | ||
+ | M& | ||
+ | M# | ||
+ | M& | ||
+ | M& | ||
+ | MXQB-[!BMY!B-[1BMY1B-[ABMYAB-[QBMYQB-\!@@G1: | ||
+ | MK> 82?]I 8WI& | ||
+ | MPA@8K=\82? | ||
+ | MPQB, | ||
+ | M 8WJ& | ||
+ | M2?]I 8WI& | ||
+ | MWQB-Z!BMXAB-ZQBMY1B-[A@@G1: | ||
+ | MYAB-[Q@@G1: | ||
+ | MI*V_& | ||
+ | M& | ||
+ | M& | ||
+ | M& | ||
+ | M& | ||
+ | MO8 : | ||
+ | M& | ||
+ | M^Y #&& | ||
+ | M_VD!A?NI * )2F;[D , | ||
+ | M: | ||
+ | M *V[& | ||
+ | M^Z3]A/ | ||
+ | ML%BD_+T & | ||
+ | M@!& | ||
+ | MI/R] !D1HY& | ||
+ | MHY& | ||
+ | M@!& | ||
+ | M | ||
+ | M " 0" 0" 0" 8! (! (! (!@$ @$ @$ @& 0" 0" 0" 8! | ||
+ | M(! (! (!@$ @$ @$ @& 0" 0" 0" 8! (! (! (!@$ @$ @$ @& 0" 0" 0" | ||
+ | M 8! (! (! (!@$ @$ @$ @& 0" 0" 0" 8! (! (! (!@$ @$ @$ @& 0" 0 | ||
+ | M" 0" 0 | ||
+ | M | ||
+ | H | ||
+ | |||
+ | end | ||
+ | |||
+ | begin 666 cube3d.s | ||
+ | M ' J*BHJ*BHJ*BHJ*BHJ*BHJ*BHJ*BHJ*BHJ*BHJ*BHJ*@TJH*" | ||
+ | MH*" | ||
+ | MH*" | ||
+ | MH' | ||
+ | M+S$Y+SDTH*" | ||
+ | MH*" | ||
+ | M3T=204V@5TE, | ||
+ | MH*" | ||
+ | M3Z" | ||
+ | M2TE.1RR@: | ||
+ | M25.@4%)/ | ||
+ | M*@TJH*" | ||
+ | MH%53(:" | ||
+ | MH*" | ||
+ | MH*" | ||
+ | MH*" | ||
+ | MH*" | ||
+ | M64]5*$^@H*" | ||
+ | MH*" | ||
+ | MH*" | ||
+ | MH*" | ||
+ | MH*" | ||
+ | M*@TJH*" | ||
+ | M5$Q%H$U/ | ||
+ | M5TE42*" | ||
+ | MH*" | ||
+ | M5T6@04Q, | ||
+ | MH*" | ||
+ | M9R D,3 P, T-*J!C3TY35$%.5%, | ||
+ | M0TA!4D%# | ||
+ | M15*@4T54# | ||
+ | M3B=4H$)%H%)53DY)3D< | ||
+ | MH$& | ||
+ | M# | ||
+ | M(" | ||
+ | M0T]54E-%+*!# | ||
+ | M.W1%35!/ | ||
+ | M5D%224%" | ||
+ | M3$E# | ||
+ | M<" | ||
+ | M+@T-86YG; | ||
+ | M15, | ||
+ | M1E-%5 T-*J!V: | ||
+ | M8F]R9& | ||
+ | M4D5%3J!-14U/ | ||
+ | M9F0R# | ||
+ | M82!=, | ||
+ | M65!215-3# | ||
+ | M# | ||
+ | M5*!!4U-%34), | ||
+ | M.V%.1*!704E4H%1/ | ||
+ | M5TE40TB@2T59# | ||
+ | M;7 @(R=8)R [; | ||
+ | M;& | ||
+ | M, | ||
+ | M0E5& | ||
+ | M8G5F9F5R# | ||
+ | M5$6@2$5210T@< | ||
+ | M+2TM+2TM+2TM+2TM+2TM# | ||
+ | M< | ||
+ | M6:!43Z Q,# | ||
+ | M, T@;& | ||
+ | M96UP, | ||
+ | M0U)%14XLH%=(251%+*!# | ||
+ | M0D4S1"< | ||
+ | M>" Y9B [0UE!3@T@=' | ||
+ | M>' | ||
+ | M0TA%0TN@3U54H%1(1: | ||
+ | M>' | ||
+ | M24Q3(2< | ||
+ | M("> | ||
+ | M)T8S+T8T)RPY, | ||
+ | M97@@, | ||
+ | M4D]4051)3TXG+# | ||
+ | M4D531513)RPP1 T@=' | ||
+ | M9# U# | ||
+ | M97@@,# -=& | ||
+ | M; | ||
+ | M; | ||
+ | M4Z!!4D6@0U524D5.5$Q9H%-%5*!54*!)3J!B87-I8PTJH$%.1*!" | ||
+ | M05-314U" | ||
+ | MH%50H" | ||
+ | M(", | ||
+ | MH$=205!(24-3# | ||
+ | M(# | ||
+ | M(&QD82 C,# -(& | ||
+ | M3U=3H$9/ | ||
+ | M8R C, | ||
+ | M142@5$^@041$H# | ||
+ | M=$^@2E5-4*!43Z!42$6@3D585*!23U< | ||
+ | M(# | ||
+ | M(& | ||
+ | M0U1%4J!.54U" | ||
+ | M5* Q-J!424U%4PT-(# | ||
+ | M4PT-(& | ||
+ | M(& | ||
+ | M35!, | ||
+ | MH$A%4D6@4T^@5$A!5*!35T%0H$)51D9%4E.@5TE, | ||
+ | M< | ||
+ | M3*!604Q515, | ||
+ | M(& | ||
+ | M# | ||
+ | M# | ||
+ | M;7 @(S$S,R [9C$_# | ||
+ | M(# | ||
+ | M25-%H$E.0U)%05-%H%@M4D]4051)3TX-(& | ||
+ | M, | ||
+ | M#2!J;7 @.F-O; | ||
+ | M;7 @(V%N9VUA>" | ||
+ | M4D]4051)3TX-(& | ||
+ | M82!D< | ||
+ | M(S$S-0T@8FYE(# | ||
+ | M; | ||
+ | M, | ||
+ | M<" Z8V]N= TZ9C< | ||
+ | M(", | ||
+ | M# | ||
+ | M8VQC# | ||
+ | M34%824U53: | ||
+ | M3RP@4D53150-.F-O; | ||
+ | M(& | ||
+ | M14%,# | ||
+ | M(V%N9VUA> | ||
+ | M> | ||
+ | M5" | ||
+ | M4TE-4$Q)1EF@3U52H$Q)1D4-861D82!M86, | ||
+ | M3T=%5$A%4@T@8VQC# | ||
+ | MH%1(1: | ||
+ | M3RR@4U5" | ||
+ | M5*!45T^@04Y' | ||
+ | M861C(" | ||
+ | M/# | ||
+ | M.W-Z# | ||
+ | M(# | ||
+ | M(# | ||
+ | M># | ||
+ | M-B [5# | ||
+ | M#2 ^/ | ||
+ | M< | ||
+ | M=#$P(#M4,3 ]4U@K4UD-# | ||
+ | M051%H& | ||
+ | M4D\-9& | ||
+ | MH$E3H$%34U5-142@5$A!5*!42$6@3E5-0D52# | ||
+ | M2$6@04-# | ||
+ | M3D5' | ||
+ | M0T]-4$Q%345.5 T@;' | ||
+ | M9@T@861C(", | ||
+ | M: | ||
+ | M; | ||
+ | M(' | ||
+ | M(", | ||
+ | M(#P\/ T-*BJ@; | ||
+ | M34E.3U*@3$5!4 TJ*J!/ | ||
+ | MH$]# | ||
+ | M, | ||
+ | M.F-A;& | ||
+ | M; | ||
+ | M>" | ||
+ | M# | ||
+ | M;W,L> T@< | ||
+ | M8R!C; | ||
+ | M+S(-(# | ||
+ | M>" | ||
+ | M9$DI+S(-.F-A;& | ||
+ | M< | ||
+ | M. T@< | ||
+ | M*%0X*2DO, | ||
+ | M8PT@;& | ||
+ | M*%0T*2ME22DO, | ||
+ | M('0Q, T@< | ||
+ | M*2\R# | ||
+ | M8R!S: | ||
+ | M(' | ||
+ | M-2DI+S(-(# | ||
+ | M(& | ||
+ | M, | ||
+ | M:" | ||
+ | M< | ||
+ | M>" [: | ||
+ | M/ | ||
+ | M861C(' | ||
+ | M.G=H97< | ||
+ | M< | ||
+ | MH$%, | ||
+ | MH$%.1*!35$]21: | ||
+ | M25.@1T545$E.1Z!43Z!" | ||
+ | M(# | ||
+ | MH%1(1: | ||
+ | M04Y$H$1%0E5' | ||
+ | M14U03U)!4EF@3$]# | ||
+ | M0U1)3TZ@4U5" | ||
+ | M+*!42$521: | ||
+ | M4Z!(15)%+@T@< | ||
+ | M3DE& | ||
+ | M9& | ||
+ | M1T6@5$A%H%-)1TZ@3T: | ||
+ | M,2 [3E5-0D52+@T@96]R(", | ||
+ | M,: Q70T@: | ||
+ | M9' | ||
+ | M,2 [*%-%1: | ||
+ | MH$& | ||
+ | M,: M,: Q70T@/ | ||
+ | M< | ||
+ | M90T@/ | ||
+ | M(& | ||
+ | M/ | ||
+ | M96< | ||
+ | M# | ||
+ | M82!T8@T@;& | ||
+ | M< | ||
+ | M<# | ||
+ | M, | ||
+ | M;& | ||
+ | M,: M, | ||
+ | M=& | ||
+ | M# | ||
+ | M9& | ||
+ | M(' | ||
+ | M< | ||
+ | M, | ||
+ | M(' | ||
+ | M549& | ||
+ | M4D%)1TA41D]25T%21" | ||
+ | M;& | ||
+ | M97(K, | ||
+ | M3$Q9+*!$4D%7H%1(1: | ||
+ | M=' | ||
+ | M(' | ||
+ | M# | ||
+ | M> | ||
+ | M# | ||
+ | M3$E.10T-(& | ||
+ | M82!T> | ||
+ | M# | ||
+ | M> | ||
+ | M=' | ||
+ | M>" [6S& | ||
+ | M< | ||
+ | M82!P-WD-(' | ||
+ | M,: M,: Q70T@< | ||
+ | M.VY)3D4-# | ||
+ | M(' | ||
+ | M< | ||
+ | M(& | ||
+ | M# | ||
+ | M=68@;& | ||
+ | M=& | ||
+ | M1: | ||
+ | M#2!J;7 @;6%I;B [85)/ | ||
+ | M+2TM+2TM+2TM+2TM+2TM+2TM+2TM+2TM+2TM+0TJH' | ||
+ | M1: | ||
+ | M8W0@8VQC# | ||
+ | M4D]4051%1*!Z# | ||
+ | M1: Q, | ||
+ | M(" [; | ||
+ | M=$%" | ||
+ | M0U1)3TX-(' | ||
+ | M8PT@;& | ||
+ | MH$E3H%)/ | ||
+ | M*F%U>" | ||
+ | M1D93152@5$A%H$-/ | ||
+ | M3TZ@5TA9H%1(25, | ||
+ | M550-*J!T87B@H# | ||
+ | MH%=(3TQ%H%1(24Y' | ||
+ | M9& | ||
+ | M4D]4051%1*!9# | ||
+ | M+S)>; | ||
+ | M5*!42$6@0T]/ | ||
+ | M3J!214%33TZ@5$^@344-*J!42$6@24Y35%)50U1)3TZ@0D5, | ||
+ | M5*!73U)++J" | ||
+ | MH' | ||
+ | M4$6@5$^@2$5# | ||
+ | M+2TM+2TM+2TM+2T-*J!S; | ||
+ | M54Q425!, | ||
+ | M-BU" | ||
+ | M1: | ||
+ | M04Y/ | ||
+ | M052@3T9& | ||
+ | M< | ||
+ | M5Z!" | ||
+ | MH$E3H%1(1: | ||
+ | M# | ||
+ | M1T%4259%H$]2# | ||
+ | M9B [: | ||
+ | M4TE4259%(0T@< | ||
+ | M4T5$H$5!4DQ)15(N# | ||
+ | MH$Y534)%4E, | ||
+ | M3$6@1D]2H$1%5$%)3%, | ||
+ | M4D6@252@3U54H%E/ | ||
+ | M(& | ||
+ | M0: | ||
+ | M8R [; | ||
+ | M/ | ||
+ | M; | ||
+ | M# | ||
+ | M76QO;W R(& | ||
+ | M# | ||
+ | M259)1$6@0EF@5$A%H$]& | ||
+ | M:R [< | ||
+ | M259%+@T@: | ||
+ | M5$A)3D> | ||
+ | M6: | ||
+ | M# | ||
+ | M54535$E/ | ||
+ | M(S P# | ||
+ | M# | ||
+ | M(' | ||
+ | M+2TM+2TM+2TM+2TM+2TM+2TM+2TM+2TM+0TJH& | ||
+ | M8: | ||
+ | M; | ||
+ | M5D526: | ||
+ | M# | ||
+ | M, | ||
+ | M=69F97(I+' | ||
+ | M; | ||
+ | M62$-(' | ||
+ | MH' | ||
+ | M9F9E< | ||
+ | M(# | ||
+ | MH$]2H$19# | ||
+ | M# | ||
+ | M2$6@1%@O, | ||
+ | M(" [; | ||
+ | M> | ||
+ | M73$@.VE& | ||
+ | M< | ||
+ | M2T6@0: | ||
+ | M(&UA8R @.W-!346@5$A)3D< | ||
+ | M; | ||
+ | M8R!D> T@8F-C(& | ||
+ | M#2 ^/ | ||
+ | M3D-214%3142@> | ||
+ | M251)04R@3$E.1: | ||
+ | MH%-4549& | ||
+ | M1: | ||
+ | M=F4L=' | ||
+ | MH$)51D9%4@T-(' | ||
+ | M># | ||
+ | M# | ||
+ | M(' | ||
+ | M;& | ||
+ | M; | ||
+ | M<B @.RAT2$E3H$-!3J!" | ||
+ | M15)%H$%21: | ||
+ | M3E.@6# | ||
+ | M2*!!H%!/ | ||
+ | M24: | ||
+ | M; | ||
+ | MH$), | ||
+ | M9& | ||
+ | M, | ||
+ | M; | ||
+ | M8F-S(' | ||
+ | MH$E.H%D-# | ||
+ | M# | ||
+ | M97(I+' | ||
+ | M6D6@5$A%H$-/ | ||
+ | MH$9/ | ||
+ | M+& | ||
+ | M;& | ||
+ | M04Y93TY%# | ||
+ | M97(I+' | ||
+ | M(# | ||
+ | M# | ||
+ | M4 T-8VQE86YU<" | ||
+ | M# | ||
+ | M0EE%(0T-*BTM+2TM+2TM+2TM+2TM+2TM+2TM+2TM+2TM+2TM+2T-*J!S3TU% | ||
+ | MH%9!4DE!0DQ%4PT-=' | ||
+ | M(# | ||
+ | M9', | ||
+ | M, | ||
+ | M(# | ||
+ | M>" | ||
+ | M,2 [=TA9H$%21: | ||
+ | M<R Q(# | ||
+ | M059)3D> | ||
+ | M<# | ||
+ | M2$6@24Y# | ||
+ | M< | ||
+ | M05)%H%1(1: | ||
+ | M(& | ||
+ | M# | ||
+ | M5$%)3%, | ||
+ | M# | ||
+ | M345.5%.@3T: | ||
+ | M,R!D<R Q# | ||
+ | M34XI# | ||
+ | M<R Q# | ||
+ | M(& | ||
+ | M<R Q# | ||
+ | M:2!D<R Q# | ||
+ | M5*!54*!" | ||
+ | M(" [< | ||
+ | M=' @;' | ||
+ | M9& | ||
+ | M9& | ||
+ | M9& | ||
+ | MH$)95$53# | ||
+ | M8D]42*!/ | ||
+ | M152@55" | ||
+ | M04), | ||
+ | # | ||
+ | |||
+ | end | ||
+ | |||
+ | ============================================================================= | ||
+ | </ | ||
+ | ====== DESIGN OF A ' | ||
+ | < | ||
+ | by Craig Bruce < | ||
+ | |||
+ | 0. PREFACE | ||
+ | |||
+ | I originally planned to write this entire article all in one go, but its | ||
+ | size, complexity, and scope of required design decisions have forced me to | ||
+ | split this article into two pieces. | ||
+ | management in writing this article). | ||
+ | I am talking about and discusses, at an abstract level, how the system will | ||
+ | work. The next part will dive into all of the nuts and bolts of the | ||
+ | low-level design. | ||
+ | |||
+ | Also, this article may be a bit to weird for some people to grasp. | ||
+ | bear with me. This article is as much a scratchpad for my rough ideas about | ||
+ | the kind of system I want to build as it is an explanatory article. | ||
+ | need a Master' | ||
+ | talk about. | ||
+ | is available via anonymous FTP from " | ||
+ | uni-tasking OS that has a Unix-like flavor. | ||
+ | working on the next release...). | ||
+ | |||
+ | One more note about the article: it is written in the present tense (" | ||
+ | rather than the future tense (" | ||
+ | read and understand. | ||
+ | design may change in many ways if the system ever is made to exist. | ||
+ | |||
+ | 1. INTRODUCTION | ||
+ | |||
+ | The full title of this article should be " | ||
+ | Microkernel Operating System for the Good Old ' | ||
+ | discussion, we will call the new operating system " | ||
+ | operating system (OS) is one that is able to execute more than one " | ||
+ | " | ||
+ | " | ||
+ | although in reality they are not, because there is only " | ||
+ | 128 and it can only do one thing at a time. | ||
+ | |||
+ | A " | ||
+ | that are connected by a network. | ||
+ | makes all of the independent computers look like ONE big computer. | ||
+ | general, a distributed system, as compared to a centralized one (like MS-DOS | ||
+ | or Unix), gives you "a higher performance/ | ||
+ | buck' | ||
+ | failure modes (if protocols are implemented correctly), shared resources, | ||
+ | incremental growth and online extensibility, | ||
+ | fact that some applications are inherently distributed." | ||
+ | my Ph.D. thesis about distributed systems of powerful workstations. | ||
+ | distributed system means increased modularity and ease of construction, | ||
+ | sharing devices like disk drives and resources like memory between multiple | ||
+ | computers, and the true parallelism of running different processes on | ||
+ | multiple computers at the same time. Not to mention " | ||
+ | |||
+ | A " | ||
+ | higher-level functionality (such as the file system) into the domain of user | ||
+ | processes. | ||
+ | (relative to a monolithic kernel). | ||
+ | |||
+ | So why would we want our OS to have the features of Multitasking, | ||
+ | Distributed, | ||
+ | interests me. The ease-of-construction thing is important too. Another | ||
+ | important question is "can it be done?" | ||
+ | be done, whenever I get around to it (one of these lifetimes). | ||
+ | |||
+ | 2. GENERAL DESIGN OVERVIEW | ||
+ | |||
+ | There are a number of high-level design decisions that must be made before | ||
+ | going into a detailed design. | ||
+ | |||
+ | 2.1. SPECIAL C-128 FEATURES | ||
+ | |||
+ | The C-128 has a minumum set of special features that make it feasible to run | ||
+ | a multitasking operating system, as opposed to earlier machines like the | ||
+ | C-64. The simplest special feature that the C128 has is *enough memory*. | ||
+ | 64K of the C64 just isn't enough. | ||
+ | enough. Expanded internal memory makes the proposition even easier. | ||
+ | |||
+ | The C-128 also has relocatable zero-page and stack-page pointers. | ||
+ | feature are absolutely essential and you could not make an effective | ||
+ | multitasking OS for any 6502 machine without it. I wonder if Commodore | ||
+ | thought about this prospect when designing the MMU chip... | ||
+ | |||
+ | The last C-128 feature is *speed*. | ||
+ | used with the 80-column VDC display. | ||
+ | properly, to make your applications zip along. | ||
+ | is not harnessed properly, see Microsloth Windoze. | ||
+ | very nice, too. Only the VDC display should be supported by a " | ||
+ | the VIC display. | ||
+ | |||
+ | 2.2. NETWORK | ||
+ | |||
+ | The OS should be designed to run on a system of between 1 and N C-128' | ||
+ | where N has a maximum of something like 8 or 16. We'll choose 16 for our | ||
+ | software design. | ||
+ | are proposing makes the step between 1 and N C-128' | ||
+ | so why not go for it. Also, if N were to become some number like 256 or | ||
+ | 65536, then we could start kicking some serious ass performance-wise, | ||
+ | certain classes of computations. | ||
+ | have already constructed a parallel-port network (a Jedi's weapon!), so I | ||
+ | might as well use it. | ||
+ | |||
+ | The required network connects the user ports of C-128' | ||
+ | completely sure how to connect more than two C-128' | ||
+ | probably need some diodes or logic gates), so the initial version of this | ||
+ | network hardware will have a maximum of two hosts. | ||
+ | to make the software of the system easily reconfigurable for any number of | ||
+ | hosts. | ||
+ | |||
+ | You will need two appropriate connectors and some 14-conductor ribbon cable | ||
+ | to build the network. | ||
+ | the type used with the VIC-20 expansion port that I sawed in half and the | ||
+ | cable is some old junk ribbon cable that was lying around that I removed some | ||
+ | of the conductors from. Any old junk will do. You're probably best off if | ||
+ | your cable is less than six feet long (2 metres). | ||
+ | follows: | ||
+ | |||
+ | C128-A | ||
+ | GND < | ||
+ | FLAG < | ||
+ | PB0 < | ||
+ | PB1 < | ||
+ | PB2 < | ||
+ | PB3 < | ||
+ | PB4 < | ||
+ | PB5 < | ||
+ | PB6 < | ||
+ | PB7 < | ||
+ | PA2 < | ||
+ | GND < | ||
+ | CNT2 < | ||
+ | SP2 < | ||
+ | PC2 < | ||
+ | |||
+ | Here is the Commodore 128 User Port when looking at the back of the unit: | ||
+ | |||
+ | 111 | ||
+ | | ||
+ | | ||
+ | | ||
+ | |||
+ | This gives a parallel bus that can operate at a peak of about 80 | ||
+ | kiloBYTES/ | ||
+ | a peak of about 21 kiloBYTES/ | ||
+ | uni-directional, | ||
+ | provided by software. | ||
+ | network, is that you can't use a modem that plugs into the user port at the | ||
+ | same time. Of course, any serious user will have a modem that plugs into a | ||
+ | UART card anyway. | ||
+ | |||
+ | You can also write your own applications for this network, since programming | ||
+ | it is quite easy; the hardware takes care of all of the handshaking. | ||
+ | blast 256 bytes over the network from C128-A to C128-B, you would: | ||
+ | |||
+ | C128-A: sender | ||
+ | ============== | ||
+ | lda #$FF ; | ||
+ | sta $DD03 sta $DD03 | ||
+ | ldy #0 ldy #0 | ||
+ | - lda DATA,y ;get data - lda #$10 ;wait for data | ||
+ | sta $DD01 ;send data - bit $DD0D | ||
+ | lda #$10 ;wait for ack beq - | ||
+ | - bit $DD0D lda $DD01 ;receive data/send ack | ||
+ | beq - sta DATA,y ;store data | ||
+ | iny ;next iny | ||
+ | bne -- bne -- | ||
+ | rts rts | ||
+ | |||
+ | These routines can even be tweaked a little more for higher performance. | ||
+ | Programming the shift register is analogous to the above. | ||
+ | |||
+ | There is probably no need to do error checking on the data transmitted over | ||
+ | the network since the cable should be about as reliable as any of the other | ||
+ | cables hanging out the back of your computer (and none of them have error | ||
+ | checking (except maybe your modem cable)). | ||
+ | |||
+ | 2.3. PROCESSES | ||
+ | |||
+ | A process is a user program that is in an active state of execution. | ||
+ | uni-tasking operating systems like ACE or the Commodore Kernal, there is only | ||
+ | one process in the entire system. | ||
+ | multiple processes. | ||
+ | program, in isolation, logically as if it were the only process in the | ||
+ | system. | ||
+ | them were used to run each program you have loaded. | ||
+ | |||
+ | In reality, there is only 1 CPU in the 128 (well, that we are interested in | ||
+ | using), so its time is divided up and given out in small chunks to execute so | ||
+ | many instructions of each program before moving onto the next one. The act | ||
+ | of changing from executing one program to executing another is called | ||
+ | " | ||
+ | one set of processor registers, so these must be saved and restored every | ||
+ | time we switch between processes. | ||
+ | must be restored and saved every time it is activated and deactivated | ||
+ | (respectively). | ||
+ | switching can be done quite efficiently (unlike with some RISC processors). | ||
+ | The maximum period of time between context switches is called the " | ||
+ | time. In our system, the quantum is 1/60 of a second. | ||
+ | a coincidence that this period is the same as the keyboard-scanning period. | ||
+ | Depending on priorities and ready processes, a new or the same old process | ||
+ | may be selected for execution after the context switch of the 60-Hz | ||
+ | interrupt. | ||
+ | |||
+ | Splitting the time of one processor among N processes may sound like we're | ||
+ | simply making each one run N times slower, which may be unbearably slow, but | ||
+ | that is not generally the case. One thing that a CPU spends a lot of its | ||
+ | time doing is *waiting*. | ||
+ | full attention of the CPU, but waiting requires absolutely no CPU attention. | ||
+ | As an example, your speedy computer spends a lot of its time waiting for its | ||
+ | slow-as-molasses-launching-into-orbit user to type a key. If we were to put | ||
+ | the process that asks the OS for a keystroke into a state of suspended | ||
+ | animation, then the CPU time that process would have consumed in a | ||
+ | busy-waiting loop can be better spent on executing the other processes that | ||
+ | are " | ||
+ | time waiting, so " | ||
+ | |||
+ | There are a number of things other than keystrokes that processes may wait | ||
+ | for in our envisioned system: modem characters, disk drive operations (if | ||
+ | they are custom-programmed correctly), mouse & joystick movements, real-time | ||
+ | delays, and interactions with other processes. | ||
+ | for processes to communicate with one another when they cannot perform some | ||
+ | operation in isolation (i.e., when they become lonely). | ||
+ | |||
+ | A process has the following things: a program loaded into the internal memory | ||
+ | of the 128, its own zero page and processor stack page, and the global | ||
+ | variables of its program. | ||
+ | various other resources of servers throughout the distributed system. | ||
+ | process is the unit of ownership, as well as execution. | ||
+ | priorities that determine how much execution time they are to be given | ||
+ | relative to other processes in the system. | ||
+ | |||
+ | Processes are allocated memory at the time of startup at a random location on | ||
+ | some random bank of internal memory on the 128. The biggest challenge here | ||
+ | is to relocate the user program to execute at the chosen address. | ||
+ | interface is available to programs on all internal banks of memory. | ||
+ | |||
+ | 2.4. APPLICATION PROGRAM INTERFACE | ||
+ | |||
+ | To take advantage of existing software, we would like our OS to provide an | ||
+ | application-program interface (API) that is identical to that of the | ||
+ | ACE-128/64 operating system. | ||
+ | developed -- as a stepping stone toward a real operating system. | ||
+ | Programmer' | ||
+ | " | ||
+ | |||
+ | Some useful software already exists for ACE, and ACE has a well-definied | ||
+ | interface and well-behaved programs. | ||
+ | little too. The ultimate goal would be to have the same API for both systems | ||
+ | so you could run software with the more functional BOS if you have a C128 and | ||
+ | 80-column monitor, or you could use the less functional ACE if you didn't | ||
+ | have all this hardware. | ||
+ | |||
+ | The software wouldn' | ||
+ | provide quite different program environments and requirements, | ||
+ | systems should be application-source-code compatible. | ||
+ | |||
+ | Because of the vast differences between a microkernel and a monolithic | ||
+ | kernel, all of the ACE system calls would be redirected to user-library calls | ||
+ | in BOS. This user library would then carry out the operations accessing | ||
+ | whatever system services are needed. | ||
+ | |||
+ | 2.5. MEMORY MANAGEMENT | ||
+ | |||
+ | The memory management of BOS is analogous to that of ACE. There are two | ||
+ | different classes of memory: near and far. Near memory is on the same bank | ||
+ | as a program and can be accessed directly by processor instructions. | ||
+ | memory can only be accessed through the kernel by the special kernel calls | ||
+ | Fetch and Stash and must be specially allocated to a process by the operating | ||
+ | system. Note that near memory is considered a sub-class of far memory; the | ||
+ | far-memory primitives can be used to access near memory. | ||
+ | |||
+ | Only the basic memory-accessing code is provided by the kernel; higher-level | ||
+ | memory management, such as dynamic memory allocation and deallocation, | ||
+ | handled by the Memory Server (below). | ||
+ | |||
+ | Unlike ACE, BOS provides the fundamental concept of " | ||
+ | The Fetch and Stash primitives can also access the memory of a remote machine | ||
+ | in a completely user-transparent way. Thus, a far-memory pointer can be | ||
+ | passed between processes on different machines, and the memory that the | ||
+ | pointer refers to can be read and written with equal programming by both | ||
+ | processes. This feature can be dangerous without a synchronization mechanism, | ||
+ | so this memory sharing is intended to be used only with the communication | ||
+ | mechanism. | ||
+ | |||
+ | There should not be an unacceptable overhead in accessing remote memory on | ||
+ | the 128 (like how there would be with bigger computers) because far-memory | ||
+ | fetching for local memory is quite expensive anyways (relative to near | ||
+ | memory), so an application will optimize its far memory accessing, and the | ||
+ | necessary interrupt handling on the remote machine can be done with very | ||
+ | little latency because of the " | ||
+ | |||
+ | 2.6. COMMUNICATION | ||
+ | |||
+ | In the type of system that is envisioned, processes are not strictly | ||
+ | independent and competitive; | ||
+ | done. To facilitiate this interprocess communication (IPC), a particular | ||
+ | organization is chosen: the Remote Procedure Call (RPC) paradigm. | ||
+ | message-passing scheme that is used with the heavily hyped Client/ | ||
+ | system architecture model. | ||
+ | place when you call a local procedure (a subroutine): | ||
+ | the processing, and the return. | ||
+ | RPC: | ||
+ | |||
+ | Send( processId, requestBuffer, | ||
+ | |||
+ | Receive( ) : processId, requestBuffer, | ||
+ | |||
+ | Reply( processId ) : err; | ||
+ | |||
+ | Send() is used to transmit a message to a remote process and get back a reply | ||
+ | message. | ||
+ | remote process to execute its request. | ||
+ | sequence of bytes whose meaning is completely defined by the user. The | ||
+ | message contents are stored in a buffer (hunk of memory) before sending, and | ||
+ | a length is specified at the time of sending. | ||
+ | message must also be allocated by the sender and specified at the time of | ||
+ | sending. | ||
+ | unnecessarily, | ||
+ | memory primitives are used to access message contents. | ||
+ | across machine boundaries because of the distributed-memory mechanism | ||
+ | described above. | ||
+ | |||
+ | Receive() is used to receive a message transmitted by a remote process to the | ||
+ | current process. | ||
+ | corresponding Send() operation, and then the request and reply buffer | ||
+ | pointers and lengths are returned. | ||
+ | contents of the request message, process the request, prepare the reply | ||
+ | message in the far-memory reply buffer, and then execute the Reply() | ||
+ | primitive. | ||
+ | receiving a message from a process and issuing the corresponding reply | ||
+ | message. | ||
+ | processes until it gets what it needs, compute pi to 10_000 decimal places, | ||
+ | and then reply to the process that sent a message to it a long time ago. | ||
+ | |||
+ | Reply() is used to re-awaken a process that sent a message that was | ||
+ | Receive()d by the current process. | ||
+ | set up the far-memory reply buffer in whatever way the sending process | ||
+ | requires prior to issuing the Reply(). | ||
+ | |||
+ | The expected usage of buffers is for the sender to use near memory for the | ||
+ | request and reply buffers and access them as regular near memory to construct | ||
+ | and interpret request and reply messages. | ||
+ | buffers as far memory (which they may very well be since processes are | ||
+ | allowed to execute on different banks of internal memory and even on | ||
+ | different machines), and may wish to fetch parts of messages into near memory | ||
+ | for processing. | ||
+ | when necessary. | ||
+ | |||
+ | And that's it. You only have this RPC mechanism for communicating with other | ||
+ | processes and for all I/O. Well, that's not entirely true; the RPC stuff is | ||
+ | hidden behind the application program interface, which provides such facades | ||
+ | as the Open and Read system calls, and a very-low level interrupt | ||
+ | notification mechanism which a user process will not normally use. | ||
+ | |||
+ | 2.7. SYSTEM SERVERS | ||
+ | |||
+ | Since all that user program has for IPC and I/O is the RPC mechanism, a | ||
+ | number of system server processes must be set up to allow a user program to | ||
+ | do anything useful. | ||
+ | user programs but provide service that is normally implemented directly into | ||
+ | the operating system kernel. | ||
+ | disadvantages to organizing a system in this way. A big advantage is that it | ||
+ | is easier to build a modular system like this, and a big disadvantage is that | ||
+ | you lose some performance to the overhead of the IPC mechanism. | ||
+ | |||
+ | A useful implication of using servers rather than having user processes | ||
+ | execute inside of the kernel is mutual exclusion. | ||
+ | serialize user requests. | ||
+ | one-at-a-time. | ||
+ | manipulated in order to provide service must not be manipulated by multiple | ||
+ | processes simultaneously or you may get inconsistent results. | ||
+ | mutually exclusive access to shared variables in a monolithic system, either | ||
+ | ugly and problematic semaphores must be used, or more-restrictive, | ||
+ | mechanisms like allowing only one user process to enter the kernel. | ||
+ | |||
+ | 2.7.1. PROCESS SERVER | ||
+ | |||
+ | This server is responsible for starting and terminating user processes. | ||
+ | Because of the way that the procedure is organized, the process server is | ||
+ | actually quite responsive dispite all of the work that must be done in order | ||
+ | to start up and terminate a user process. | ||
+ | |||
+ | The server is highly integrated with the kernel, and it is able to do things | ||
+ | that regular user processes cannot (like manipulate kernel data structures), | ||
+ | but it still functions as an independent entity, as a regular user process. | ||
+ | Its code is physically a part of the kernel for bootstrapping purposes, since | ||
+ | it can hardly be used to start itself. | ||
+ | |||
+ | When you wish to run a new program, a request message is sent to the process | ||
+ | server. | ||
+ | arguments to the new program, environmental variables, and a synchronous/ | ||
+ | asynchronous flag. If you want to run a sub-process synchronously, | ||
+ | process server does not reply to your request until the new process | ||
+ | terminates. | ||
+ | your request as soon as the new process is created. | ||
+ | quite useful in Unix (although Unix has a more complicated mechanism for | ||
+ | providing the service) (think "&" | ||
+ | provided here. | ||
+ | |||
+ | The process server allocates and initializes the kernel data structures | ||
+ | necessary for process management, and then starts the process running | ||
+ | bootstrapping code in the kernel. | ||
+ | known to be trustworthy. | ||
+ | program file, reading the memory requirements, | ||
+ | reading in the program file, relocating the program for whatever memory | ||
+ | address it happened to load in at (bank relocation is no problem) and | ||
+ | far-calling the main routine (finally). | ||
+ | kill the process. | ||
+ | |||
+ | Since the process bootstraps itself, the process server' | ||
+ | process creation procedure is minimal, and the process server is ready to | ||
+ | process new requests with minimal delay (maximal responsiveness). | ||
+ | self-bootstrapping user process concept comes from my Master' | ||
+ | Another advantage of having a process server is that you can start a process | ||
+ | running on any machine from any other machine in exactly the same way you | ||
+ | would start a process on the local machine; we have achieved transparentness, | ||
+ | Park. | ||
+ | |||
+ | The process server also takes care of process destruction (exit or kill) and | ||
+ | provides other less-significant services, like reading and setting the | ||
+ | current date and time. The mechanism by which process destruction is done is | ||
+ | similar to the self-bootstrapping idea and is discussed, probably | ||
+ | inappropriately, | ||
+ | |||
+ | The server is located by having a well-known address. | ||
+ | id is a constant and hard-coded into clients. | ||
+ | integer values, for each machine (a machine-id is encoded into process ids), | ||
+ | and these integers are indexes into a small look-up table with the actual | ||
+ | addresses for well-known addresses, so the process ids aren't pinned but can | ||
+ | be used as if they were pinned. | ||
+ | |||
+ | 2.7.2. MEMORY SERVER | ||
+ | |||
+ | The memory server handles the dynamic allocation and deallocation of far | ||
+ | memory. | ||
+ | memory that it can use, and the server gets the memory, sets the ownership to | ||
+ | the process, and returns a pointer. | ||
+ | by a process is handled easily. | ||
+ | |||
+ | There is also a call that deallocates all memory owned by a certain user | ||
+ | process. | ||
+ | memory of the user program is be deallocated along with the rest of the | ||
+ | process' | ||
+ | types and banks (later) of memory it has used so that bulk deallocation can | ||
+ | be done efficiently when the process exits. | ||
+ | |||
+ | A client process can also ask that far memory be allocated on a remote | ||
+ | machine. | ||
+ | convenient when you need LOTS of memory for a process. | ||
+ | get at this remote memory is to simply send a message directly to the remote | ||
+ | memory server of the machine you want to allocate memory on, and this does | ||
+ | indeed work, so this is what we will do. But, this doesn' | ||
+ | that you have allocated memory on a far machine by itself, and we don't want | ||
+ | to waste any effort in freeing all of the memory, both local and remote, that | ||
+ | a process owns when it terminates; i.e., we don't want to send deallocation | ||
+ | requests to all remote memory servers just to be sure. | ||
+ | |||
+ | There are a few alternatives for solving this problem, but I think this is a | ||
+ | good place for a quick-and-dirty hack. Whenever a user process sends a | ||
+ | message to a memory server (both local or remote, for whatever reason), | ||
+ | through the memory servers' | ||
+ | the machine number (0-15) in a special 16-bit field of the sender' | ||
+ | control block is set. Then, when the process terminates, the termination | ||
+ | procedure (next) peeks at this special field and sends free-all messages to | ||
+ | all remote memory servers that the process in question has interacted with. | ||
+ | This insures that all memory in the entire distributed system that is | ||
+ | allocated to a process is tidied up when the process terminates. | ||
+ | process server, the memory server is integrated with the kernel. | ||
+ | |||
+ | MORE PROCESS TERMINATION | ||
+ | |||
+ | Come to think of it, I should talk more about process termination. | ||
+ | idea would probably be for a user process to terminate itself, in the same | ||
+ | way that it bootstraps itself. | ||
+ | process that wants to kill someone to the process server. | ||
+ | situation for a process to commit suicide. | ||
+ | the process id to be terminated and the exit code for the termination. | ||
+ | |||
+ | The process server then suspends the doomed process' | ||
+ | process' | ||
+ | code inside of the kernel. | ||
+ | the process has opened through the standard library calls (and other server- | ||
+ | resources held), deallocates all memory held by the process, maybe does some | ||
+ | other cleanup work, and then sends a special message to the process server to | ||
+ | remove the process control block. | ||
+ | special message from the process that is terminating after the first phase of | ||
+ | the process shutdown has been completed, to insure a proper termination. | ||
+ | process control block is then deallocated and may be used again. | ||
+ | server is the only process that is allowed to manipulate process control | ||
+ | blocks. | ||
+ | |||
+ | Come to think of it, there is a slight problem with process initialization: | ||
+ | getting a copy of the arguments and environmental variables for an | ||
+ | asynchronously started new process. | ||
+ | before the new process has had a chance to make a copy of the arguments and | ||
+ | environment, | ||
+ | that sends the reply message back to the parent process. | ||
+ | |||
+ | 2.7.3. FILE SERVERS | ||
+ | |||
+ | Each disk drive in the system has a special server that provides an interface | ||
+ | for executing Open, Read, Write, Close, and a number of other common file | ||
+ | operations. | ||
+ | reclamation for processes that die. There are a few ways to provide this, | ||
+ | and each has implications about the overall design of a server. | ||
+ | |||
+ | One possibility is to have " | ||
+ | does not keep track of, for example, which files a process has open or the | ||
+ | current file positions. | ||
+ | the file to be used, positions to the section of the file, performs the | ||
+ | operation, and closes the file again. | ||
+ | some intelligent caching makes it work efficiently. | ||
+ | dies without closing all of its files, it doesn' | ||
+ | be closed anyway, logically at the completion of each operation. | ||
+ | approach doesn' | ||
+ | for devices for which we don't have a custom device driver, so we won't use | ||
+ | it. | ||
+ | |||
+ | Another possibility is to have " | ||
+ | (my own invention). | ||
+ | files are open but doesn' | ||
+ | the already-opened file is positioned according to the request and the file | ||
+ | operation takes place. | ||
+ | block (FCB) is left behind, but the FCB will be closed and reused after a | ||
+ | certain period of time. If the client actually hasn't died, then the | ||
+ | situation will be detected (through details not explained here) and the file | ||
+ | will be reopened as if nothing has happened. | ||
+ | process' | ||
+ | unreliable communication service. | ||
+ | Commodore-DOS very well. | ||
+ | |||
+ | The final design considered is to have a registry of servers that that a | ||
+ | process has resources currently allocated on be associated with each process. | ||
+ | When a client makes an open request to the server (or some equivalent | ||
+ | resource-grabbing operation), the server checks to see if the client is | ||
+ | currently holding any other of the server' | ||
+ | request is processes normally. | ||
+ | server' | ||
+ | machine telling the process server to record the fact that the client is (or | ||
+ | may be) holding some of the server' | ||
+ | the server' | ||
+ | the client terminates, it will send a standard " | ||
+ | that I am (may be) holding on this server" | ||
+ | client' | ||
+ | etc. | ||
+ | |||
+ | In this " | ||
+ | would contain both an open file entry and the file position information, | ||
+ | files would always open and close when we intuitively expect them to. It is | ||
+ | assumed that the communication mechanism is reliable, which it is here. This | ||
+ | mechanism *does* model Commodore-DOS well. In fact, this idea is so nice | ||
+ | that I may redesign the memory allocation recovery mechanism to use this. | ||
+ | There is a slight possibility of a "race condition" | ||
+ | nothing bad can happen because of it. (This is just a note to myself: make | ||
+ | it so that if a process is killed while it is receive- or reply-blocked, | ||
+ | ignore the reply from the server if the process id is reused; damn, there' | ||
+ | still a potential problem; I'll have to figure it out later; also watch out | ||
+ | for a distributed deadlock on the PCB list). | ||
+ | |||
+ | So, our server supports the regular file operations and implements them in | ||
+ | pretty much the expected way, since it is a " | ||
+ | of the server accepts a request, determines which type it is, extracts the | ||
+ | arguments, calls the appropriate local procedure, prepares the reply message, | ||
+ | replies, and goes back to the top of the loop. Each opened file is | ||
+ | identified by a user process by a file control block number that has meaning | ||
+ | inside of the server, as per usual. | ||
+ | " | ||
+ | " | ||
+ | a close operation takes place. | ||
+ | reference count reaches zero. Our system will not implement any security at | ||
+ | this time. | ||
+ | |||
+ | Because of the abstraction of sending formatted messages to a server, | ||
+ | different types of disk drives (Commodore-DOS, | ||
+ | all dealt with in exactly the same way. As one slight extension, we have to | ||
+ | hack our devices (at least some of them) a little to be able to handle | ||
+ | " | ||
+ | described next. | ||
+ | |||
+ | 2.7.4. PREFIX SERVER | ||
+ | |||
+ | The prefix server idea is stolen from the computer science literature about a | ||
+ | network operating system called " | ||
+ | pathname lookup service for the pathnames of different disk-file and device | ||
+ | servers. | ||
+ | on a system of multiple distributed file servers. | ||
+ | "mount table" in Unix. Its prefix table looks something like the following: | ||
+ | |||
+ | PREFIX | ||
+ | ------ | ||
+ | / < | ||
+ | / | ||
+ | /fd1 < | ||
+ | |||
+ | BTW, BOS uses Unix-style filenames rather than the Creative-Micro-Designs- | ||
+ | style filenames that ACE uses. | ||
+ | |||
+ | If an application is given an absolute pathname, it will consult the prefix | ||
+ | server to resolve it to the process-id of an actual server. | ||
+ | pathname "/ | ||
+ | pathname " | ||
+ | relative pathname "" | ||
+ | |||
+ | The user process would then contact the appropriate server with the relative | ||
+ | pathname. | ||
+ | while the system is running, so some intelligent caching can be done. Also, | ||
+ | directory tokens are given out for executing a " | ||
+ | and these server/ | ||
+ | searches. | ||
+ | searches always follow through correctly. | ||
+ | |||
+ | 2.7.5. DEVICE SERVERS | ||
+ | |||
+ | Device servers are just another type of file server, except they control a | ||
+ | specific device other than a regular disk device, and they are likely to | ||
+ | support some custom operations and return error codes if some disk operations | ||
+ | are attempted. | ||
+ | |||
+ | 2.7.6. CONSOLE SERVER | ||
+ | |||
+ | Just a specific device server. | ||
+ | calls, like WinClear, WinPut, GetKey, and ConWrite, that are used in ACE. | ||
+ | |||
+ | 2.8. ASYNCHRONOUS EVENT HANDLING | ||
+ | |||
+ | As mentioned in the Process section above, there are many external events | ||
+ | that a process may have to wait for, including: | ||
+ | operations (if they are custom-programmed correctly), mouse & joystick | ||
+ | movements, and real-time delays. | ||
+ | primitive to allow a process to wait for one of these events to happen. | ||
+ | Normally, the only processes that wait for these events will be device | ||
+ | drivers. | ||
+ | some devices (like the modem and keyboard) to insure that things don't become | ||
+ | unnecessarily inefficient. | ||
+ | |||
+ | 3. KERNEL DESIGN | ||
+ | |||
+ | Next time. | ||
+ | |||
+ | 4. SYSTEM SERVER DESIGN | ||
+ | |||
+ | Next time. | ||
+ | |||
+ | 5. APPLICATION PROGRAM INTERFACE | ||
+ | |||
+ | Next time. | ||
+ | |||
+ | This is quite similar to the ACE-128/64 Programmer' | ||
+ | is available via anonymous FTP from " | ||
+ | "/ | ||
+ | at the time of writing this article. | ||
+ | |||
+ | 6. CONCLUSION | ||
+ | |||
+ | Next time. | ||
+ | |||
+ | Implementation: | ||
+ | |||
+ | ==================================================================---END---=== | ||
+ | </ |
magazines/chacking8.txt · Last modified: 2015-04-17 04:34 by 127.0.0.1