4827 lines
196 KiB
Plaintext
4827 lines
196 KiB
Plaintext
########
|
|
##################
|
|
###### ######
|
|
#####
|
|
##### #### #### ## ##### #### #### #### #### #### #####
|
|
##### ## ## #### ## ## ## ### ## #### ## ## ##
|
|
##### ######## ## ## ## ##### ## ## ## ## ##
|
|
##### ## ## ######## ## ## ## ### ## ## #### ## ##
|
|
##### #### #### #### #### ##### #### #### #### #### #### ######
|
|
##### ##
|
|
###### ###### Issue #9
|
|
################## Jan. 24, 1995
|
|
########
|
|
|
|
------------------------------------------------------------------------------
|
|
Editor's Notes
|
|
by Craig Taylor
|
|
|
|
And *drum beat please* here's another issue of Commodore Hacking!! We've
|
|
lasted longer and had more issues put out than some other magazines I won't
|
|
discuss (*wondering where issue 39 of that mag is*).
|
|
|
|
Not many Commodore notes this time - things have gotten a little bit more
|
|
montatenous(sp - it's late) on the Commodore front.
|
|
|
|
I was unable to get an article by Craig Bruce in time but I got the next
|
|
best thing: An interview with him!! Users of his software may find this
|
|
interview interesting in how he looks at programming.
|
|
|
|
Right now I'm entertaining the thought of dropping C= Hacking after I
|
|
graduate which will be sometime around July 1st of this year. I'm interested
|
|
in somebody who would "carry the reign" so to speak, and take over my job of
|
|
nagging, bugging people etc :-) to write articles. I've got my system fairly
|
|
automated in handling requests here - if that person has a VAX account then
|
|
I could set them up with a mailserver, if a UNIX account then there's oodles
|
|
of them floating on the net that could be used. _PLEASE_ write to me and
|
|
lemme know if you're interested. I'm going to try to get one more issue of
|
|
Commodore Hacking out before July 1st.
|
|
|
|
===========================================================================
|
|
|
|
Please note that this issue and prior ones are available via anonymous FTP
|
|
from ccosun.caltech.edu (amongunders) under /pub/cbm/hacking.mag and via a
|
|
mailserver which documentation can be obtained by sending mail to
|
|
"duck@pembvax1.pembroke.edu" with a subject line of "mailserver" and the
|
|
lines of "help" and "catalog" in the body of the message.
|
|
|
|
===========================================================================
|
|
Legal Mumbo-Jumbo
|
|
|
|
Permission is granted to re-distribut3e this "net-magazine", in 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 /
|
|
diskettte costs for this "net-magazine".
|
|
|
|
===========================================================================
|
|
In This Issue:
|
|
|
|
Commodore Trivia Corner
|
|
|
|
This edition of Commodore Trivia Corner contains the answers to the July
|
|
edition of trivia ($070 - $07F), the questions and answers for August ($080 -
|
|
$08F), September ($090 - $09F), October ($0A0 - $0AF), November ($0B0 - $0BF),
|
|
and the questions for the December edition ($0C0 - $0CF). Enjoy them!
|
|
|
|
A Different Perspective, part II
|
|
|
|
This month George and Steve continue their series on 3D graphics on the C-64
|
|
with a look at hidden faces and filled faces. In addition to adding these
|
|
features into last month's program some other improvements to the old program
|
|
will be discussed, such as fast multiplication (around 24 cycles) and various
|
|
bug fixes -- for instance, the program now works on older C-64's which
|
|
initialize color RAM to the background color when the screen is cleared (sorry
|
|
about that ;-). A very primitive form of texture mapping is also included. As
|
|
usual, full source and executables are included. The native C64 files are in a
|
|
Lynx archive, so you will obviously need Lynx to get at them -- check your
|
|
favorite BBS or FTP site.
|
|
|
|
2D Graphics Toolbox: Circles
|
|
|
|
To augment three-dimensional algorithms this series will focus on
|
|
two-dimensional drawing algortihms. Circles are the subject this
|
|
time around (heh -- get it?), and a very fast algorithm for drawing
|
|
them on your C64 is presented, with examples in assembly and BASIC7.0.
|
|
How fast is fast? How does 11 cycles per pixel without the use of
|
|
tables grab ya?
|
|
|
|
AFLI=specs v1.0
|
|
|
|
In AFLI we can get 120 colors in theory (counted like this
|
|
16!/(2!*14!)=120). When we put red and blue hires pixels close to
|
|
each other we get a vision of purple - thanks the television. This article
|
|
details what AFLI is, how it's used and done.
|
|
|
|
Coding Tricks
|
|
|
|
Included are a series of postings to comp.sys.cbm about neat coding tricks (in
|
|
machine language) that are interesting and useful.
|
|
|
|
C.S.Bruce Interview
|
|
|
|
An interview with the author of Zed, the ACE os and many other numerous
|
|
utilities for the Commodore 64/128.
|
|
|
|
Aligning 1541 Drives
|
|
|
|
A discussion regarding Commodore 1541 disk drive alignment procedures, with
|
|
suggestions.
|
|
===========================================================================
|
|
Commodore Trivia Corner
|
|
by Jim Brain (brain@mail.msen.com)
|
|
|
|
Well, it is a new year, and I am sending up a new collection of the
|
|
Commodore rivia for all to enjoy. If you haven't seen this already, the
|
|
following is a collection of trivia questions that I post to various
|
|
networks every month. I have collected Trivia Edition #8-13 in this
|
|
article. As you may know, these questions form part of a contest in which
|
|
the monthly winner gets a prize (Thanks to my various prize donators).
|
|
The whole thing is mainly just for fun, so please enjoy.
|
|
|
|
As the new year rolls in, I am happy to report the following:
|
|
|
|
1) As I have gained access to FIDONet, the trivia is now posted to both
|
|
the USENET newsgroup COMP.SYS.CBM on the Internet AND the FIDONet echo
|
|
CBM every month.
|
|
|
|
2) A number of publications have started publishing the trivia, including
|
|
Commodore World, and a variety of club newsletters.
|
|
|
|
3) I have moved into my new house (See new address at bottom). While this
|
|
may not seem important, the extra room I now have means I can now bring
|
|
all of my old CBM machine to the new house and work with them. Working
|
|
with them gives me fodder for more trivia.
|
|
|
|
As always, I welcome any questions (with answers), and encourage people
|
|
to enter their responses to the trivia, now at #13. Be sure you get the
|
|
responses to me by January 12th at noon.
|
|
|
|
|
|
Jim.
|
|
|
|
|
|
The following article contains the answers to the July edition of trivia
|
|
($070 - $07F), the questions and answers for August ($080 - $08F), September
|
|
($090 - $09F), October ($0A0 - $0AF), November ($0B0 - $0BF), and the
|
|
questions for the December edition ($0C0 - $0CF). Enjoy them!
|
|
|
|
|
|
Here are the answers to Commodore Trivia Edition #8 for July, 1994
|
|
|
|
Q $070) On a PET series computer, what visual power-on indication will tell
|
|
the user whether the computer has Revision 2 or Revision 3 ROMs?
|
|
|
|
A $070) Revision Level 2 ROMS (the ones with more bugs) power up with:
|
|
*** COMMODORE BASIC ***, with '*' in place of the more familiar
|
|
'#' character.
|
|
|
|
Q $071) The IEEE-488 interface is sometimes called the GPIB interface.
|
|
What does GPIB stand for?
|
|
|
|
A $071) General Purpose Interface Bus. Another name is Hewlett Packard
|
|
Interface Bus (HPIB), since HP developed this standard for its
|
|
istrumentation device networking.
|
|
|
|
Q $072) Commodore manufactured at least two hard drives with IEEE-488
|
|
interfaces. Can you name them?
|
|
|
|
A $072) The Commodore D9060 and D9090. From the cbmmodel.txt file:
|
|
|
|
* CBM D9060 5 MB Hard Drive, DOS3.0, Off-White, IEEE-488. GP
|
|
* CBM D9090 7.5 MB Hard Drive, DOS3.0, Off-White, IEEE-488. GP
|
|
|
|
The following model has been said to be in existence, though no one
|
|
has one on hand to prove it:
|
|
|
|
* CBM D9065 7.5 MB Hard Drive
|
|
|
|
And this model may never have made it past the prototype stage:
|
|
|
|
CBM D9062 Dual D9065.
|
|
|
|
Q $073) Why didn't buyers like the original PET-64?
|
|
|
|
A $073) It looked just like a old-style C-64. It had a "home" computer
|
|
look that the schools didn't care for. They liked the "business"
|
|
look of the PET series, so Commodore put refurbished and new 64
|
|
motherboards in PET cases and sold them as PET 64s. The repackaging
|
|
suited the schools.
|
|
|
|
Q $074) On a PET Revision 2 ROM, what was the largest single array size that
|
|
BASIC could handle?
|
|
|
|
A $074) An array can have a cumulative total of 256 elements. For single
|
|
dimension arrays, that means D(0) to D(255), but a 2D array can only
|
|
go from DD(0,0) to DD(1,127) etc. All types of arrays had this
|
|
limitation.
|
|
|
|
Q $075) On the stock 1541, data is transmitted one bit at a time. How many
|
|
bits are transferred at a time on the Commodore 1551 disk drive?
|
|
|
|
A $075) 3 bits were transmitted at a time. I assume that each byte had a
|
|
parity bit tacked on for error detection, so it would have taken
|
|
3 transfers to transmit a byte of information from the drives.
|
|
|
|
Q $076) On all Commodore floppy disk drives, how fast does the disk spin?
|
|
|
|
A $076) 300 RPM.
|
|
|
|
Q $077) Upon first reading the Commodore 1541 Error channel after turning
|
|
on the disk drive, what error number and text is returned?
|
|
|
|
A $077) 73, CBM DOS V2.6 1541, 0, 0
|
|
|
|
Q $078) What error number and text is returned on a 1551?
|
|
|
|
A $078) 73, CBM DOS V2.6TDISK, 0, 0 Notice that the new text JUST fits!
|
|
|
|
Q $079) Commodore printers are normally assigned to device #4, but they
|
|
can be also used as device #?
|
|
|
|
A $079) #5. The Commodore 1525 has a switch to do this, but not all printers
|
|
have such a switch.
|
|
|
|
Q $07A) What microprocessor is used in the Commodore 1551 disk drive?
|
|
|
|
A $07A) the 6510T. It is a slight variant on the 6510 microprocessor used
|
|
on the C64. Some say it runs at 2 MHz, but the specs drives spec
|
|
sheet doesn't say.
|
|
|
|
Q $07B) When the VIC-20 was designed, the serial port throughput was roughly
|
|
equivalent to the throughput of the IEEE-488 bus? Why isn't it
|
|
very fast in production VICs?
|
|
|
|
A $07B) Let's go back to question $04F:
|
|
|
|
<begin insert>
|
|
Q $04F) What was the primary reason Commodore went to a serial bus
|
|
with the introduction of the VIC-20?
|
|
|
|
A $04F) Jim Butterfield supplied me with this one:
|
|
|
|
As you know, the first Commodore computers used the IEEE bus
|
|
to connect to peripherals such as disk and printer. I
|
|
understand that these were available only from one source:
|
|
Belden cables. A couple of years into Commodore's computer
|
|
career, Belden went out of stock on such cables (military
|
|
contract? who knows?). In any case, Commodore were in quite
|
|
a fix: they made computers and disk drives, but couldn't
|
|
hook 'em together! So Tramiel issued the order: "On our next
|
|
computer, get off that bus. Make it a cable anyone can
|
|
manufacture". And so, starting with the VIC-20 the serial
|
|
bus was born. It was intended to be just as fast as the
|
|
IEEE-488 it replaced.
|
|
<end insert>
|
|
|
|
And here is what Jim Butterfield followed up with:
|
|
|
|
"Technically, the idea was sound: the 6522 VIA chip has a "shift
|
|
register" circuit that, if tickled with the right signals (data and
|
|
clock) will cheerfully collect 8 bits of data without any help from
|
|
the CPU. At that time, it would signal that it had a byte to be
|
|
collected, and the processor would do so, using an automatic
|
|
handshake built into the 6522 to trigger the next incoming byte.
|
|
Things worked in a similar way outgoing from the computer, too.
|
|
We early PET/CBM freaks knew, from playing music, that there was
|
|
something wrong with the 6522's shift register: it interfered with
|
|
other functions. The rule was: turn off the music before you start
|
|
the tape! (The shift register was a popular sound generator). But
|
|
the Commodore engineers, who only made the chip, didn't know this.
|
|
Until they got into final checkout of the VIC-20.
|
|
|
|
By this time, the VIC-20 board was in manufacture. A new chip could
|
|
be designed in a few months (yes, the silicon guys had application
|
|
notes about the problem, long since), but it was TOO LATE!
|
|
|
|
A major software rewrite had to take place that changed the VIC-20
|
|
into a "bit-catcher" rather than a "character-catcher". It called for
|
|
eight times as much work on the part of the CPU; and unlike the shift
|
|
register plan, there was no timing/handshake slack time. The whole
|
|
thing slowed down by a factor of approximately 5 to 6.
|
|
|
|
When the 64 came out, the problem VIA 6522 chip had been
|
|
replaced by the CIA 6526. This did not have the shift register
|
|
problem which had caused trouble on the VIC-20, and at that time it
|
|
would have been possible to restore plan 1, a fast serial bus. Note
|
|
that this would have called for a redesign of the 1540 disk drive,
|
|
which also used a VIA. As best I can estimate - and an article in
|
|
the IEEE Spectrum magazine supports this - the matter was discussed
|
|
within Commodore, and it was decided that VIC-20 compatibility was
|
|
more important than disk speed. Perhaps the prospect of a 1541
|
|
redesign was an important part of the decision, since current
|
|
inventories needed to be taken into account. But to keep the
|
|
Commodore 64 as a "bit-banger", a new problem arose.
|
|
|
|
The higher-resolution screen of the 64 (as compared to the VIC-20)
|
|
could not be supported without stopping the CPU every once in a while.
|
|
To be exact: Every 8 screen raster lines (each line of text), the CPU
|
|
had to be put into a WAIT condition for 42 microseconds, so as to
|
|
allow the next line of screen text and color nybbles to be swept into
|
|
the chip.(More time would be needed if sprites were being used).
|
|
But the bits were coming in on the serial bus faster than that:
|
|
a bit would come in about every 20 microseconds! So the poor CPU,
|
|
frozen for longer than that, would miss some serial bits completely!
|
|
Commodore's solution was to slow down the serial bus even more.
|
|
That's why the VIC-20 has a faster serial bus than the 64, even though
|
|
the 64 was capable, technically, of running many times faster.
|
|
|
|
Fast disk finally came into its own with the Commodore 128."
|
|
|
|
--Jim
|
|
|
|
Q $07C) On Commodore computers, how much RAM is set aside as a tape buffer?
|
|
|
|
A $07C) 192 bytes is used as a tape buffer. Blocks of data on tape are 192
|
|
bytes long.
|
|
|
|
Q $07D) On Commodore computers, most every peripheral has a device number.
|
|
What is the device number of the screen?
|
|
|
|
A $07D) #3
|
|
|
|
Q $07E) What is the device number of the keyboard?
|
|
|
|
A $07E) #0
|
|
|
|
Q $07F) Commodore computers use 2's-complement notation to represent integers.
|
|
What is the 2's-complement hex representation of the signle byte -1?
|
|
|
|
A $07F) (This was not a Commodore specific question) Commodore computers
|
|
use this notation to represent integer quantities. In 2's complement
|
|
notation, a -1 looks like 11111111(binary) or $FF(hex).
|
|
|
|
|
|
Here are the answers to Commodore Trivia Edition #9 for August, 1994
|
|
|
|
Q $080) During the days of the Commodore 64 and the VIC-20, Commodore
|
|
produced at least two Commodore magazines. What were their names?
|
|
|
|
A $080) The magazines were originally called "Commodore Microcomputers" and
|
|
"Power/Play: Commodore Home Computing". They never did seem to nail
|
|
down the name of the latter as I see "Power/Play" and
|
|
"Commodore: Power/Play" used as the original names as well. Anyway,
|
|
Commodore Microcomputers started its life in 1979, whereas
|
|
"Power/Play" started in 1981. Both magazines were published until
|
|
around 1987, when they were merged to form "Commodore Magazine".
|
|
Then, around 1990, the magazine was sold to IDG Communications and
|
|
was merged into RUN. RUN was continued for a while, but was finally
|
|
pulled out of circulation. Creative Micro Designs purchased the
|
|
rights to the magazine, and now Commodore World is being produced by
|
|
CMD. I am not sure how strong (if any) a link there is between
|
|
RUN and CW, but some of the same authors write for the new
|
|
publication. Just for added info, here are the ISSN numbers:
|
|
|
|
Commodore Microcomputers (Commodore Magazine) 0744-8724
|
|
Power/Play:Commodore Home Computing 0739-8018
|
|
RUN (Commodore/RUN) 0741-4285
|
|
|
|
"The Transactor" is also a correct answer, and info on it is below.
|
|
|
|
Q $081) Back in the PET heyday, another magazine was produced by Commodore
|
|
Canada. This magazine was later sold and showed up as a hardware
|
|
journal. Name the magazine.
|
|
|
|
A $081) The infamous "Tarnsactor". One of the noted C64 hardware-hacking
|
|
magazines, it was originally published by Commodore Canada, before
|
|
being sold to an individual named Mr. Hilden. Its ISSN number is
|
|
0838-0163. As far as I can tell, this magazine, died many deaths,
|
|
but ceased to exist in 1989-90. Its first issue is dated April 30,
|
|
1978.
|
|
|
|
Q $082) The Commodore 128 has a VIC-II compatible chip inside it. Can this
|
|
chips be switched for a VIC-II from a Commodore 64?
|
|
|
|
A $082) No! The newer 128 compatible chip (VIC-IIe) has 8 extra pins to
|
|
perform timing functions specific for the 128. In addition, some of
|
|
the registers have extra functions. However, a suitable card
|
|
to make it compatible can be made.
|
|
|
|
Q $083) What does the video encoding standard PAL expand to?
|
|
|
|
A $083) Phase Alternating Line is the answer I was looking for, which
|
|
describes the video encoding used in Europe, but Programmable Array
|
|
Logic is also correct, which describes the family of chips used as
|
|
"glue" logic for the C64 I/O and processing chips.
|
|
|
|
Q $084) How many buttons were present on the earliest of Commodore tape decks?
|
|
|
|
A $084) 5: Play, Rewind, Fast-Forward, Record, and Stop/Eject. Later models
|
|
separated the stop and eject functions into two buttons.
|
|
|
|
Q $085) Earlier SID chips had a distinctive "clicking" sound that some demo
|
|
coders used to an advantage. Commodore subsequently removed the
|
|
click, and then later reintroduced it. When does the telltale click
|
|
occur?
|
|
|
|
A $085) When you change the volume of a voice. The voice need not be
|
|
outputting anything.
|
|
|
|
Q $086) What does CP/M stand for?
|
|
|
|
A $086) Take your pick:
|
|
|
|
Control Program/Monitor
|
|
Control Program for Microprocessors
|
|
Control Program for Microcomputers.
|
|
|
|
The last one is considered by many to be most correct.
|
|
|
|
Q $087) What is the highest line number allowed for a program line in
|
|
Commodore BASIC V2?
|
|
|
|
A $087) Normally, the user cannot enter a line number higher than 63999.
|
|
If you want to be tricky, however, the numbers can be made to go up
|
|
to 65535.
|
|
|
|
Q $088) What symbol, clearly printed on the front of a key on the Commodore
|
|
VIC, 64, and 128 keyboard, is not available when the lower case
|
|
character set is switched in?
|
|
|
|
A $088) The PI symbol. It is [SHFT-UPARROW] in uppercase mode, but becomes
|
|
a checkerboard-like character when in lower-case mode. Unlike the
|
|
graphics characters printed on the fronts of the keys, this one is
|
|
positioned in the middle of the keycap, and should probably be
|
|
accessible in both character sets.
|
|
|
|
Q $089) How do you get the "checkmark" character ?
|
|
|
|
A $089) In lowercase mode, type a shift-@
|
|
|
|
Q $08A) On the PET computers, what memory location holds the Kernal ROM
|
|
version?
|
|
|
|
A $08A) It is different from the 64/128. It is 50003. 0 here indicates old
|
|
ROMs, while 1 indicates new ROMs.
|
|
|
|
Q $08B) The Commodore computers have 2 interrupts, called IRQ and NMI.
|
|
What does IRQ stand for?
|
|
|
|
A $08B) Interrupt ReQuest. This interrupt is used for things that should
|
|
usually be allowed to interrupt the processor. This interrupt can
|
|
be masked off by the SEI instruction.
|
|
|
|
Q $08C) What does NMI stand for?
|
|
|
|
A $08C) Non-Maskable Interrupt. Unlike the IRQ, this interrupt cannot be
|
|
masked by an instruction. However, some tricks can be used to
|
|
mask it.
|
|
|
|
Q $08D) The 6502 line of microprocessors has a number of flags that can be
|
|
used to test for certain conditions. One of then is the N flag.
|
|
What does it stand for?
|
|
|
|
A $08D) 'N' stands for Negative. On instructions that change this flag, it
|
|
is set to be equal to bit 7 of the result of the instruction.
|
|
|
|
Q $08E) How about the D flag?
|
|
|
|
A $08E) It stands for decimal mode. This mode causes certain instructions
|
|
to treat a byte as 2 4 bit BCD-coded nybbles.
|
|
|
|
Q $08F) The shorthand for the BASIC keyword PRINT is '?'. What is the
|
|
shorthand equivalent for PRINT#?
|
|
|
|
A $08F) pR is the way to abbreviate PRINT#. Note that ?# will fail.
|
|
|
|
|
|
Here are the answers to Commodore Trivia Edition #10 for September, 1994
|
|
|
|
Q $090) The 6502 has a rich history. It is modeled after another 8-bit
|
|
microprocessor. Name the processor.
|
|
|
|
A $090) The 65XX series of processors was modeled after the Motorola 6800.
|
|
Motorola hampered the design groups' efforts to pursue product
|
|
developments using the 6800. A core group of 8 designers left Motorola
|
|
and went to MOS Technologies, which was the largest producer of
|
|
calculator chips at the time. MOS decided it was time to go into
|
|
the CPU business.
|
|
|
|
Q $091) The 6502 has a older brother that was never produced. Name its
|
|
number designation and why it was not produced.
|
|
|
|
A $091) The older brother to the 6502 was the 6501. The 6501 was
|
|
pin-compatible with the 6800, which prompted a suit by Motorola.
|
|
Eventually, MOS reached an agreement where they scrapped the 6501
|
|
marketing, but were free to market the 6502.
|
|
|
|
Q $092) How many different opcodes are considered valid and "legal" on the
|
|
MOS NMOS 6502 line?
|
|
|
|
A $092) 151 opcodes are documented in the NMOS 6502 data book. The remaining
|
|
105 opcodes were not implemented, and exist as "don't care" states
|
|
in the opcode matrix. That means that some seemingly invalid
|
|
opcodes will actually perform pieces of two or more valid opcodes.
|
|
Newer CPU systems trap all non-implemented opcode usages, but not
|
|
the 6502.
|
|
|
|
Q $093) Every instruction takes at least __ cycles to complete. Fill in
|
|
the missing number.
|
|
|
|
A $093) 2. The architecture assumes that each opcode has two bytes in it and
|
|
one byte can be fetched per cycle. For instructions that use only
|
|
1 byte, the extra fetched byte (actually the next opcode), is thrown
|
|
away.
|
|
|
|
Q $094) Which instructions take more time than necessary as a result of the
|
|
answer to Q $093?
|
|
|
|
A $094) Although this is a subjective answer, One could nominate NOP on the
|
|
basis that NOP is generally believed to waste one execution cycle on
|
|
a particular processor, namely one cycle on the 65XX line. However,
|
|
one can argue that NOP simply means no operation, and has no ties to
|
|
length of execution. You be the judge.
|
|
|
|
All other instructions must take at least two cycles: one for opcode
|
|
fetch, one for operation.
|
|
|
|
Q $095) What did MOS Technologies manufacture befor introducing the 650X line
|
|
of microprocessors?
|
|
|
|
A $095) As stated above, it was calculator chips.
|
|
|
|
Q $096) Three companies manufactured the 6502 under a cross-licensing
|
|
agreement. Name them.
|
|
|
|
A $096) Rockwell, MOS Technologies, and Synertek.
|
|
|
|
Q $097) In NTSC-land, how fast does the 1MHz 6510 in the C64 actually run?
|
|
|
|
A $097) 1.022727143 MHz. It is derived by taking the main clock frequency
|
|
(14.31818MHz) and diving it by 14.
|
|
|
|
Q $098) What about in PAL-land?
|
|
|
|
A $098) 985.248449 kHz. It is derived by taking the main clock frequency
|
|
(17.734472MHz) and dividing it by 18. Thus the PAL 64 actually runs
|
|
slower than the NTSC one.
|
|
|
|
Q $099) Data is latched into the 650X microprocessor on the (rising/falling)
|
|
edge?
|
|
|
|
A $099) Data is latched in to the 65XX on the falling edge of Phi0 (Phi1).
|
|
The timing diagram in some books (64 PRG is one) is incorrect.
|
|
|
|
Q $09A) Through the years, the 650X line has changed family numbers, yet
|
|
the part has not been changed. (A family number is the upper 2
|
|
digits in this case) Name the other family numbers used by MOS to
|
|
denote the 650X line.
|
|
|
|
A $09A) the 75XX line used in the 264 series (Plus/4 and C16), and the 85XX
|
|
series used in the C64C and C128 series.
|
|
|
|
Q $09B) Consider the following code:
|
|
|
|
ldx #10
|
|
lda $ff,x
|
|
|
|
what location does the accumulator get loaded with?
|
|
|
|
A $09B) The answer is location $ff+10 mod 256 = $09.
|
|
The answer involves explaining a (mis)features of the NMOS 65XX CPU
|
|
line. The above code instructs the 65XX CPU to use zero-page
|
|
addressing mode to load the accumulator. In zero-page addressing, the
|
|
address need only be one byte wide ($ff in this case), because the
|
|
high byte is considered to be $00. Now, as humans, we would expect
|
|
the CPU would add 10 to 255 ($ff), giving 265 ($109) as the address
|
|
to load the accumulator from. However, the CPU designers decided
|
|
that zero-page addressing means that the high byte will be $00 all the
|
|
time, no exceptions. If a situation like the above occurs, the
|
|
low byte of the addition will be used as the low byte of the address
|
|
(9 in this case), but the high-byte will be ZERO. All zero page
|
|
addressing modes work this way. Note that the CMOS versions of the
|
|
6502 do perform the high byte "fix-up", so this behavior is only seen
|
|
on the NMOS parts.
|
|
|
|
Q $09C) What about the following?
|
|
|
|
ldx #10
|
|
lda ($ff),x
|
|
|
|
A $09C) This was a trick. The code is trying to use INDIRECT INDEXED indexing
|
|
mode using the x register, but that addressing mode can only be used
|
|
with the y register. If the code is changed to the following, legal
|
|
code:
|
|
|
|
ldx #10
|
|
lda ($ff),y
|
|
|
|
Then, the above discussion for zero-page addressing holds true here
|
|
as well. The effective address would have been (hi:lo) $100:$0ff, but
|
|
is instead (hi:lo) $000:$0ff. The simple rule is: zero page means
|
|
exactly that. There is no way to address outside of zero-page with
|
|
zero-page addressing.
|
|
|
|
Q $09D) How many CPU clock signal lines does the 650X require to run?
|
|
|
|
A $09D) 1. The 6501 used two, as the 6800 used two, but the 6502 and
|
|
successors only required Phi0 (Phi1). Phi2 was generated on the CPU.
|
|
|
|
Q $09E) Where does the 650X line fetch its first byte from after reset?
|
|
|
|
A $09E) $fffc. The address formed by reading $fffd and $fffc is stuffed into
|
|
the IP, and the code is read starting there. $fffc is read first,
|
|
since the 65XX line stores addresses in low byte, high byte format.
|
|
|
|
Q $09F) One of the original designers on the NMOS 6502 CPU now heads up
|
|
Western Design Center in Arizona, and makes the 65C02 and 65C816
|
|
CPU chips. Name him. Hint: it is not Chuck Peddle!
|
|
|
|
A $09F) Bill Mensch. He hand-designed these newer parts in the 65XX line
|
|
in the same manner he and Chuck Peddle and others hand-designed the
|
|
6501 and 6502.
|
|
|
|
|
|
Here are the answers to Commodore Trivia Edition #11 for October, 1994
|
|
|
|
Q $0A0) In the mid 1980's, Commodore introduced RAM Expansion Units for the
|
|
Commodore 64, 64C, 128, and 128D. There were three of them. Give
|
|
their model numbers, and what was different among them.
|
|
|
|
A $0A0) The 1700 (128kB), the 1764 (256kB), and the 1750 (512kB). The
|
|
1700 and the 1750 were marketed for the 128, while the 1764 was
|
|
marketed from the 64 line.
|
|
|
|
Q $0A1) Some of the CIA integrated circuits used on the C64 and C128
|
|
computers have a hardware defect. What is the result of this
|
|
defect, and when does it occur? (May be more than one, but I need
|
|
only one)
|
|
|
|
A $0A1) The only one I have documented in front of me is the timer B
|
|
interrupt bug, which is explained in the "Toward 2400" article
|
|
by George Hug in Transactor 9.3. (1) However, I had many people
|
|
relate other bugs (2 and 3), which I haven't been able to test, so I
|
|
add them as possibilities. (I encourage readers to confirm/deny the
|
|
latter 2.)
|
|
|
|
1) If timer B of the 6526 CIA times out at about the same time as a
|
|
read of the interrupt register, the timer B flag may not be set at
|
|
all, and no interrupt will occur if timer B interrupts were
|
|
turned on.
|
|
|
|
2) When the hour on the TOD clock is 12, the AM/PM must be reversed
|
|
from its normal setting to set/reset the AM/PM flag.
|
|
|
|
3) The TOD clock sometimes generates double interrupts for alarm
|
|
trigger.
|
|
|
|
|
|
Q $0A2) Name the Commodore machine(s) on which a Intel 8088 was an OPTIONAL
|
|
coprocessor. (Hint, not the IBM clones)
|
|
|
|
A $0A2) I was looking for the B series computers, which contains the B
|
|
computers (B128, B256), as well as the 600 series and the 700
|
|
series. These computers could be fitted with an optional 8088
|
|
processor on a separate card. However, another correct answer is
|
|
the Amiga, which can have a 8088 attached via an expansion card or a
|
|
SideCar(tm) unit.
|
|
|
|
Q $0A3) On Commodore computers beside the Plus/4 series, there are three
|
|
frequencies used to record the data on the tape. Name the
|
|
frequencies used.
|
|
|
|
A $0A3) 1953.125Hz, 2840.909Hz, and 1488.095Hz. These correspond to
|
|
waveforms with periods: 512us, 352us, and 672us, respectively.
|
|
|
|
Q $0A4) Commodore Plus/4 series computers can not read any cassettes
|
|
recorded on other Commodore computers. Why? (Hint: It has
|
|
nothing to do with the nonstandard connecotr on the Plus/4)
|
|
|
|
A $0A4) The tones recorded on the Plus/4-C16 are exactly one-half the
|
|
frequencies shown above. This suggests to many that the Plus/4
|
|
and C16 were supposed to run at twice its present frequency,
|
|
but were downgraded at the last-minute, and the code to generate
|
|
the tones was not updated to reflect the change. This is just
|
|
heresay, so you decide for yourself.
|
|
|
|
Q $0A5) During power-up, the Commodore 64 checks to see if it running
|
|
in PAL-land or NTSC-land. How does it determine its location?
|
|
|
|
A $0A5) It sets the raster compare interrupt to go off at scan line 311.
|
|
If the interrupt occurs, we are on a PAL system, since NTSC will
|
|
never get to line 311 (NTSC only has 262.5 lines per frame, every
|
|
other frame shifted down a bit to create 525 lines).
|
|
|
|
Q $0A6) What is the 65XX ML opcode for BRK?
|
|
|
|
A $0A6) $00, or 00
|
|
|
|
Q $0A7) On the 65XX CPU, what gets pushed onto the stack when an interrupt
|
|
occurs?
|
|
|
|
A $0A7) The program counter gets saved high byte first, then the processor
|
|
status flags get saved.
|
|
|
|
Q $0A8) Speaking of the stack, where is the stack located in the 65XX address
|
|
map?
|
|
|
|
A $0A8) $0100 to $01FF
|
|
|
|
Q $0A9) On the 65XX CPU line, it is possible to set and clear a number of
|
|
processor status flags. Examples include SEC and CLC to set and
|
|
clear the carry flag. What flag has a clear opcode, but no set
|
|
opcode?
|
|
|
|
A $0A9) The overflow flag: V. However, the V flag can be set via an external
|
|
pin on some members of the 65XX line. The 1541 uses this as an
|
|
ingenious synchronization tool.
|
|
|
|
Q $0AA) When saving a text file to tape, the computer records 192 bytes of
|
|
data, an inter-record gap, and then the same 192 bytes of data
|
|
again. How wide is this inter-record gap, and why is it there?
|
|
|
|
A $0AA) Some terminology: "inter" means "between". Most everyone knows
|
|
that a tape block is recorded twice on the tape, but Commodore
|
|
considers the two copies and the gap between them a single
|
|
"record". Thus, this question is referring to the gap in between
|
|
two dissimilar records. With that in mind,
|
|
the interrecord gap is nominally 2 seconds long, (or 223.2 byte
|
|
lengths, although the gap contains no data). It is there to allow
|
|
the tape motors to get up to speed before the next data comes under
|
|
the read/write head. The tape motors may need to stop between
|
|
records if the program is not requesting any more data from the
|
|
tape data file at this time. If the program subsequently asks
|
|
for data from the tape, the drive must get up to speed before the
|
|
read can occur. Note: on the first version of PET BASIC, the
|
|
gap was too small, so programmers had problems retrieving data
|
|
files.
|
|
|
|
For completeness, the "intra-record" gap (The one between the two
|
|
copies of the data) consists of 50+ short pulses, each of which is
|
|
352us in length, giving a timing of .0176s+. This time was used to
|
|
copy important data to safe locations, reset pointers, and do error
|
|
logging. The entire "record" is recorded in 5.7 seconds.
|
|
|
|
Q $0AB) On an unexpanded VIC-20, where does the screen memory start?
|
|
|
|
A $0AB) $1e00, or 7680
|
|
|
|
Q $0AC) In Commodore BASIC, what is the abbreviated form of the "Load"
|
|
command?
|
|
|
|
A $0AC) lO (L SHIFT-O)
|
|
|
|
Q $0AD) In Commodore BASIC, what is the abbreviated form of the "List"
|
|
command?
|
|
|
|
A $0AD) lI (L SHIFT-I)
|
|
|
|
Q $0AE) On the Commodore 64, there is section of 4 kilobytes of RAM that
|
|
cannot be used for BASIC programs. It is the favorite hiding
|
|
places for many ML programs, however. What is its address in
|
|
memory?
|
|
|
|
A $0AE) $c000, or 49152
|
|
|
|
Q $0AF) What is stored at locations $A004-$A00B, and why is it strange?
|
|
|
|
A $0AF) The text "CBMBASIC" is stored there. It is strange because this
|
|
text is not referenced by any routine. It can also be called
|
|
strange because the code is Microsoft's. Doesn't it make you wonder?
|
|
|
|
|
|
Here are the answers to Commodore Trivia Edition #12 for November, 1994
|
|
|
|
Q $0B0) What will happen if you type ?""+-0 into the CBM BASIC interpreter
|
|
on the PET series, the 64 series, or the 128 series?
|
|
|
|
A $0B0) The BASIC interpreter has a bug in it that shows up while interpreting
|
|
the above statement. The interpreter leaves two bytes on the CPU
|
|
stack prior to returning from a subroutines call. At least on the
|
|
C64, the two bytes are both zeros. Since subroutines put the return
|
|
address on the stack, the return retrieves the two bytes left on the
|
|
stack and attempts to se that as the return address. So, depending on
|
|
what code it executes after the return, it can do a number of things.
|
|
|
|
Most of the time after the bug occurs, the interpreter limps along
|
|
for a while until it hits a BRK instruction, $00. Then, that
|
|
instruction causes the system to execute an interrupt. On the C64,
|
|
the system vectors through $316-$317 (BRK vector) and does a warm
|
|
start. On the C128 and PETs with Monitors, the system dumps into the
|
|
internal machine language monitor. If the machine under use did not
|
|
do something with the BRK vector, the machine will hang.
|
|
|
|
Now, note that the above is not the only result. Since the
|
|
interpreter is executing code from the wrong location, any result
|
|
from no effect to hung machine is possible.
|
|
|
|
Note that this is NOT normal behavior. The system should report an
|
|
error while interpreting the above statement.
|
|
|
|
Q $0B1) In the first CBM 64 units, what color was the screen color RAM
|
|
changed to when you cleared the screen?
|
|
|
|
A $0B1) The screen color RAM was changed to value 1 when the screen was
|
|
cleared. Thus, when a byte was poked into screen RAM, the resulting
|
|
character was white on the screen. The white contrasted nicely
|
|
with the normal blue background.
|
|
|
|
Q $0B2) Why was it changed in later versions of the 64?
|
|
|
|
A $0B2) Commodore found that this practice sometimes caused "light flashes"
|
|
during screen scrolls. I was going to leave this for another time,
|
|
but ... The change was to make the color RAM equal to background
|
|
color register #0. Well, this got rid of the "light flashes", but
|
|
then poking values to screen RAM caused invisible characters, since
|
|
the foreground color of the character was the same as the background
|
|
color of the screen.
|
|
|
|
Well, this broke a number of older programs that did not
|
|
properly initialize the color RAM. Also, Commodore fixed the problem
|
|
with the VIC-II that had caused these "light flashes" So, Commodore
|
|
changed the KERNAL a third time. Since the above change caused
|
|
invisible characters, Commodore made a third revision that changed
|
|
the color RAM to the value in location 646 (the current cursor
|
|
foreground color).
|
|
|
|
Q $0B3) What is "special" about the text that displays the "illegal quantity
|
|
error" in CBM BASIC?
|
|
|
|
A $0B3) The text is actually "?ILLEGAL QUANTITY ERROR". Notice the two
|
|
spaces between "QUANTITY" and "ERROR". John West supplies the
|
|
expanantion:
|
|
|
|
"The vector at $0300 points to a routine at $A43A, which is the
|
|
general error message printing routine. Load .X with the number of
|
|
the error, and it prints it. it looks up the address of the error
|
|
text from a table, then prints the text, which does not have any
|
|
trailing spaces. It then prints ' ERROR', with *2* spaces. It
|
|
does this for all errors."
|
|
|
|
Historically, this effect is caused by the VIC-20, which only had 22
|
|
columns. When the VIC-20 BASIC was being ported from the PET BASIC
|
|
code, someone noticed that the some of the error strings would
|
|
span two VIC-20 lines. So, the BASIC error messages were changed
|
|
a little, so that they all printed neatly on two lines: The PET
|
|
error string:
|
|
?ILLEGAL QUANTITY ERROR (one space) became:
|
|
?ILLEGAL QUANTITY
|
|
ERROR (carriage return plus one space).
|
|
When the C64 BASIC was being ported from the VIC-20, the carriage
|
|
return was replaced with a space character.
|
|
|
|
I admit this caught me by surprise. I have used Commodore computers
|
|
for years, and never noticed that "?SYNTAX ERROR" had 2 spaces in it.
|
|
|
|
Q $0B4) On what Commodore machine was the operating system OS/9 available?
|
|
|
|
A $0B4) Since OS/9 was a real-time operating system for the 6809
|
|
microprocessor, it was available on only one Commodore machine, which
|
|
had two different names: The Commodore SuperPET. The machine was
|
|
sold as the "MMF (Micro MainFrame) 9000 in Germany, and its model
|
|
number was SP9000.
|
|
|
|
Q $0B5) Which Commodore machine(s) does not have a user port?
|
|
|
|
A $0B5) There were a number of answers to this question, and there may be
|
|
more:
|
|
|
|
The Commodore C16. Commodore decided to cut out telecommunications,
|
|
and thus designed the user port out of the computer, as the modem is
|
|
the only use Commodore ever made of the user port. This also
|
|
includes the C116, a version of the C16 with a chicklet keyboard.
|
|
|
|
The Commodore Ultimax/MAX machine. This was the ill-fated game
|
|
console produced in the early 80s. It was basically a stripped down
|
|
Commodore 64.
|
|
|
|
The 64 GS (Game System). This machine was another flop produced
|
|
in the late 80s.
|
|
|
|
Q $0B6) How many pins are there in a Commodore Serial Connector?
|
|
|
|
A $0B6) 6.
|
|
|
|
Q $0B7) There are 13 addressing modes available on the 6502. Name them.
|
|
|
|
A $0B7) No# Name Description
|
|
--- ------------ -----------
|
|
01) accumulator asl a
|
|
02) immediate lda #$00
|
|
03) zero page lda $00
|
|
04) zero page,X lda $00,X
|
|
05) zero page,Y lda $00,Y
|
|
06) absolute lda $1000
|
|
07) absolute,X lda $1000,X
|
|
08) absolute,Y lda $1000,Y
|
|
09) implied clc
|
|
10) relative bne
|
|
11) (indirect,X) lda ($00,X)
|
|
12) (indirect),Y lda ($00),Y
|
|
13) (absolute indirect) jmp ($1000)
|
|
|
|
Q $0B8) If you were to put one large sequential file onto an 8050 disk drive,
|
|
how big could that file be?
|
|
|
|
A $0B8) According to the 8050 User Manual, a sequential file could be
|
|
521208 bytes in size.
|
|
|
|
Q $0B9) How many characters can be present in a standard Commodore DOS
|
|
filename?
|
|
|
|
A $0B9) 16 characters.
|
|
|
|
Q $0BA) How many pins does a 6502 IC have on it?
|
|
|
|
A $0BA) 40 pins.
|
|
|
|
Q $0BB) How many pins does the standard IEEE-488 connector have on it?
|
|
|
|
A $0BB) 24 pins.
|
|
|
|
Q $0BC) On the IEEE-488 bus, what does the acronym for pin 7, NRFD, stand for?
|
|
|
|
A $0BC) Not Ready For Data.
|
|
|
|
Q $0BD) On the NMOS 6502, what is the ML opcode for SED, and what does this
|
|
opcode do?
|
|
|
|
A $0BD) $f8, SEt Decimal mode. Sets the D flag in the status flags byte.
|
|
Although used rarely, this opcode switches on Binary Coded Decimal
|
|
mode. In BCD mode, the byte $10 is treated as 10, not 16. The add
|
|
and subtract instructions are the only legal ones affected by this
|
|
mode, although some undocumented/illegal opcodes are also affected.
|
|
For example, in this mode, adding the byte $15 (21) to the byte $25
|
|
(37) yields $40 (64) not $3a (58). emember that, in this mode,
|
|
$40 = 40, not 64.
|
|
|
|
Q $0BE) Assuming a PET computer and a non-PET computer have access to a
|
|
common disk drive or tape drive, there are two ways to load a PET
|
|
BASIC program on the non PET CBM computer. Name them.
|
|
|
|
A $0BE) Most differing series of Commodore computers had different places
|
|
for the start of BASIC programs. For instance, on the C64, $0801
|
|
(2049) is the start of BASIC memory, but most PET computers start
|
|
BASIC memory at $0401 (1025). This wouldn't matter, except that
|
|
BASIC programs are stored on tape and disk with the start address,
|
|
and the line links in a BASIC program have absolute addresses in them.
|
|
To fix these problems, the Commodore VIC-20 and newer computers came
|
|
out with a "relocatable load". So, here are the two choices:
|
|
|
|
1) Save the program on the PET like so: save "name",X (X is device).
|
|
Then, you could load the program into the non-PET machine
|
|
by using a relocatable load: load "name",X. This would load the
|
|
program in at start of BASIC memory and refigure the line links.
|
|
|
|
2) Redefine start of BASIC memory on non-PET machine. A couple
|
|
of pokes to relevant BASIC pointers, and the start of BASIC
|
|
was moved. Then, load the program non-relocatable.
|
|
|
|
Now, from the above discussion, it looks like option 1 is the
|
|
simplest route. Well, it would be, exept for one small detail:
|
|
Earlier PET computers saved the BASIC program from $0400, not
|
|
$0401 as is expected. The effect: loading relocatable on a non-PET
|
|
would have a zero byte as the first byte of the program. The quick
|
|
fix: change BASIC pointer to itself-1, load PET program, reset
|
|
BASIC pointer. Commodore didn't make it easy!
|
|
|
|
Q $0BF) Only one of the ways detailed in $0BE works the other way around.
|
|
Which one?
|
|
|
|
A $0BF) Since the earlier PET computers did not have a "relocatable load",
|
|
the only way to load a program from, say, a C64 into an 2001 was to
|
|
use option #2 above and move the start of BASIC memory to $0801
|
|
(2049).
|
|
|
|
|
|
Commodore Trivia Edition #13
|
|
|
|
Q $0C0) The early 1541 drives used a mechanism developed by ______. Name
|
|
the company.
|
|
|
|
Q $0C1) On later models, Commodore subsequently changed manufacturers
|
|
for the 1541 drive mechanism. Name the new manufacturer.
|
|
|
|
Q $0C2) What is the most obvious difference(s). (Only one difference is
|
|
necessary)
|
|
|
|
Q $0C3) On Commodore BASIC V2.0, what answer does the following give:
|
|
PRINT (SQR(9)=3)
|
|
|
|
Q $0C4) In Commodore BASIC (Any version) what does B equal after the following
|
|
runs: C=0:B=C=0
|
|
|
|
Q $0C5) The first PET cassette decks were actually _______ brand cassette
|
|
players, modified for the PET computers. Name the comapny.
|
|
|
|
Q $0C6) In Commodore BASIC (Any version), what happens if the following
|
|
program is run:
|
|
|
|
10 J=0
|
|
20 IF J=0 GO TO 40
|
|
30 PRINT "J<>0"
|
|
40 PRINT "J=0"
|
|
|
|
Q $0C7) In question $068, we learned how Jack Tramiel first happened upon the
|
|
name "COMMODORE". According to the story, though, in what country
|
|
was he in when he first saw it?
|
|
|
|
Q $0C8) On the Commodore user port connector, how many edge contacts are
|
|
there?
|
|
|
|
Q $0C9) On most Commodore computers, a logical BASIC line can contain up to
|
|
80 characters. On what Commodore computer(s) is this not true?
|
|
|
|
Q $0CA) If a file is saved to a Commodore Disk Drive with the following
|
|
characters: chr$(65);chr$(160);chr$(66), what will the directory
|
|
entry look like?
|
|
|
|
Q $0CB) What is the maximum length (in characters) of a CBM datasette
|
|
filename?
|
|
|
|
Q $0CC) How many keys are on a stock Commodore 64 keyboard?
|
|
|
|
Q $0CD) Commodore BASIC uses keyword "tokens" to save program space. Token
|
|
129 becomes "FOR". What two tokens expand to include a left
|
|
parenthesis as well as a BASIC keyword?
|
|
|
|
Q $0CE) There are 6 wires in the Commodore serial bus. Name the 6 wires.
|
|
|
|
Q $0CF) On the Commodore datasette connector, how many logical connections are
|
|
there?
|
|
|
|
Some are easy, some are hard, try your hand at:
|
|
|
|
Commodore Trivia Edition #13!
|
|
|
|
Jim Brain
|
|
brain@mail.msen.com
|
|
602 North Lemen (New address)
|
|
Fenton, MI 48430
|
|
(810) 737-7300 x8528
|
|
|
|
==============================================================================
|
|
A Different Perspective, part II
|
|
by George Taylor (aa601@cfn.cs.dal.ca) and Stephen Judd (sjudd@nwu.edu).
|
|
|
|
We... are... VR Troopers! Okay Troopers, once again we need to make an
|
|
excursion out of the three dimensional world and into our own little virtual
|
|
world inside the C64. So sit back in your virtual chair, put on your virtual
|
|
thinking helmet, maybe grab a virtual beer, and prepare for a virtually
|
|
useful experience with another virtually humongous article.
|
|
|
|
Last time we laid down the foundations of 3D graphics: rotations and
|
|
projections. In this article we will build upon this foundation with a look
|
|
at hidden surfaces as well as filled surfaces. In addition we will snaz up
|
|
the old program so that it is a little more efficient by for instance
|
|
introducing a much faster multiplication routine and moving all variables
|
|
into zero-page.
|
|
|
|
To get us in the mood let's review from last time. We are in a
|
|
three-dimensional space; in particular, a right-handed three-dimensional
|
|
space, so that the x-axis comes towards you, the y-axis increases to the
|
|
right, and the z-axis increases "up". Now we have some object, centered at
|
|
the origin.
|
|
|
|
To rotate the object we derived a 3x3 matrix for each axis which describes a
|
|
rotation about that axis. After rotating we translate the object along the
|
|
z-axis and then project it through the origin onto a plane z=constant.
|
|
|
|
As you recall the projection of a point is done by drawing a line from the
|
|
point through the origin, and then figuring out where this line intersects
|
|
our plane z=constant. You can think of this line as a ray of light bouncing
|
|
off the object and through our little pinhole camera lens.
|
|
|
|
Clearly for any solid object some parts of the object will remain hidden,
|
|
though, i.e. when you look at your monitor you can't see the back of it, and
|
|
you probably can't see the sides. How do we capture this behavior
|
|
mathematically?
|
|
|
|
|
|
Hidden Surfaces
|
|
---------------
|
|
|
|
Imagine our object with some light shining on it -- when will a part of the
|
|
object be hidden? Clearly it is hidden when the light reflected off of it
|
|
never reaches our eyes, which happens whenever a part of the object is
|
|
"turned away" from us. How do we express this mathematically? Consider a
|
|
flat plate, like your hand (you also might think of a cube). Now imagine a
|
|
rod sticking out of the plate, exactly perpendicular to the plate (take your
|
|
index finger from your other hand, and touch it to your palm at a
|
|
ninety-degree angle). Now rotate the plate around, and imagine the light
|
|
bouncing off and heading towards your eyes.
|
|
|
|
No matter where you place your hand in space, the very last point at which it
|
|
is visible is when it is exactly parallel to the light rays coming from it to
|
|
your eyes; or, to put it another way, when the light rays are exactly
|
|
perpendicular to a normal vector to the surface (in the above case this
|
|
vector is either a rod or your finger). If the angle between the normal and
|
|
a light ray is less than ninety degrees, then the surface is visible. If
|
|
greater, then the surface is invisible.
|
|
|
|
At this point you may be wondering how to figure out the angle between two
|
|
vectors. It turns out we really don't have to calculate it at all: instead
|
|
we use a very important tool in our mathematical toolbox, the dot product.
|
|
|
|
If we have two vectors v1=(x1,y1,z1) and v2=(x2,y2,z2) then the dot product
|
|
is defined to be
|
|
|
|
v1 dot v2 = x1*y1 + x2*y2 + x3*y3
|
|
|
|
note that this is a _scalar_ (i.e. a number), and not a vector. You can also
|
|
show that
|
|
|
|
v1 dot v2 = |v1|*|v2|*cos(theta)
|
|
|
|
where | | denotes length and theta is the angle between the two vectors.
|
|
Since cos(theta) is positive or negative depending on whether or not theta is
|
|
less than or greater than ninety degrees, all we have to do is take the dot
|
|
product and look at the sign.
|
|
|
|
But we need to understand something about the dot-product. theta is the
|
|
angle between two vectors joined at their base; mathematically the way we are
|
|
going to draw the light ray is to draw a line FROM the origin TO a point on
|
|
the surface. In our model above, we are going to draw a line from your eyes
|
|
to the palm of your hand and then slide the normal vector down this line
|
|
until the base of the normal vector touches your eye.
|
|
|
|
The whole point of this is that when we look at the dot product we need to
|
|
keep in mind that if the dot product is negative, the face is visible.
|
|
|
|
All that remains is to write down an equation: let's say that we've rotated
|
|
the surface and know a point P=(x,y,z) on the rotated surface, and we have a
|
|
normal vector to the surface vn=(vx,vy,vz). First we need to translate down
|
|
the z-axis so that P -> (x,y,z-z0) = P - (0,0,z0). If we then take the dot
|
|
product we find that
|
|
|
|
P' dot vn = (P dot vn) - z0*vz
|
|
|
|
But (P dot vn) is simply a constant: because these are rigid rotations the
|
|
length of P never changes, presumably the length of vn never changes, and the
|
|
angle between the two never changes. So introduce a constant K where
|
|
|
|
K = (P dot vn)/z0
|
|
|
|
so that all we need to do is subtract the z-component of the normal vector
|
|
from K and check if it is positive or negative: if negative, the face is
|
|
visible. Note that if we translate by an amount P + (0,0,z0) (instead of -
|
|
(0,0,z0)) we simply add the two together.
|
|
|
|
We seem to have left something out here: how do we calculate the normal
|
|
vector vn? One way to do it is by using another vector operator, the
|
|
cross-product. The dot product of two vectors is just a scalar, but the
|
|
cross product of two vectors is another vector, perpendicular to the first
|
|
two.
|
|
|
|
The most common way to visualize the cross-product is by using your right
|
|
hand: imagine two vectors in space, and place your right hand along one of
|
|
them, with your thumb sticking out. Now curl your fingers towards the other
|
|
vector. Your thumb points in the direction of the vector formed from the
|
|
cross-product of the first two. You can easily convince yourself then that
|
|
(A x B) = -(B x A), that is, if you reverse the order of the cross product,
|
|
you get a vector pointing in the opposite direction.
|
|
|
|
Therefore, if we take any two vectors in the face (in particular, we know the
|
|
edge of the face), and then take their cross-product, we have a normal
|
|
vector.
|
|
|
|
But because we are dealing with a cube, we have an even easier method! We
|
|
can use the fact that the faces on a cube are perpendicular to each other: if
|
|
we take two points and subtract them we get a vector going between the two
|
|
points. On a cube, this will give us a normal vector if we use two
|
|
"opposite" points. Therefore all we need to do is rotate the cube, subtract
|
|
two z-coordinates, add to K, and check if it is positive or negative.
|
|
|
|
This is how the program does it, and the specifics will be explained later.
|
|
Right now I want to show you a second method of hidden surface detection.
|
|
Instead of using the three-dimensional rotate vectors, what if we use the
|
|
two-dimensional _projected_ vectors? If we take the cross-product of two of
|
|
these vectors we get a vector which either points into the screen or out of
|
|
it, which corresponds to a positive or a negative result.
|
|
|
|
The cross-product is usually done by taking the determinant of a matrix. I
|
|
am not going to explain that here -- you can look in any decent calculus book
|
|
for the full cross-product. All we really care about is the z-coordinate of
|
|
the vector, and the z-coordinate of v1 x v2 is:
|
|
|
|
v1x*v2y - v1y*v2x
|
|
|
|
Whether or not the face is visible depends on how you define v1 and v2!
|
|
Always remember that (v1 x v2) = -(v2 x v1).
|
|
|
|
What is this quantity anyways? Consider a parallelogram made up of our two
|
|
vectors v1 and v2. The magnitude of the cross-product just happens to be
|
|
|
|
|v1|*|v2|*sin(theta)
|
|
|
|
which you can easily see is the area of a parallelogram with sides v1 and v2.
|
|
For this reason the second method apparently goes by the name SAM -- Signed
|
|
Area Method. (Now you need to think about the interpretation of the dot
|
|
product in a similar way).
|
|
|
|
Note that the second method is quite general, while the first method only
|
|
works for objects which have perpendicular surfaces (at least, in it's
|
|
current form presented here). On the other hand, the first method is
|
|
significantly faster.
|
|
|
|
Now that we've hidden the faces, it's time to fill them:
|
|
|
|
|
|
Filled Faces
|
|
------------
|
|
|
|
Q: How do you make a statue of an elephant?
|
|
A: Start with a block of granite and carve away everything
|
|
that doesn't look like elephant!
|
|
|
|
The first method of filling faces is very simple in concept. Let's say we
|
|
want a cube with white faces and black edges. Before, the program would make
|
|
the buffer black and then draw in the white edges. The idea here is to make
|
|
the entire buffer white, draw the edges in black, and then make everything
|
|
outside of the edges black. Quite simply, we start with a solid block and
|
|
then take away everything that doesn't look like a cube! You can also think
|
|
of it like a cookie cutter: we press our cube-shaped cutter down and remove
|
|
all the dough outside of the cutter.
|
|
|
|
This simplistic method actually has some advantages. If the object is very
|
|
large, we spend very little time doing the actual un-filling. We don't care
|
|
about how complicated the object is, because we just trace out the edge.
|
|
Finally, this gives us an extremely easy way of implementing a rudimentary
|
|
texture-mapping in multicolor mode. For instance, instead of coloring the
|
|
block white, what if we used a changing pattern of colors? As long as the
|
|
edge is a specific color, the pattern can be any combination of the other
|
|
three colors. An example program which does just this is included -- note
|
|
that the inititalization program needs to be changed slightly to run this
|
|
program.
|
|
|
|
In other words, we roll the dough, draw a pattern into it, press our cutter
|
|
down and remove the outside dough. We are left with a cube with patterns all
|
|
over it.
|
|
|
|
On the downside it's not quite so easy to do things like have each face a
|
|
separate color (but who wants wimpy separate colors when you can have
|
|
evolving texture patterns, eh? :).
|
|
|
|
The program makes a few refinements to this technique. For instance, instead
|
|
of coloring the entire buffer white, it calculates ahead of time the minimum
|
|
and maximum values for y, and only colors that part of the drawing area
|
|
white.
|
|
|
|
For the sake of completeness, here is another method of filling:
|
|
exclusive-or. A clever way of filling faces is to use some EOR magic. Let's
|
|
say we want to fill everything between two points A and B. We want to start
|
|
filling at point A and stop at point B, and since EOR is a bit flipper this
|
|
gives us a means of filling. Consider the following situation in memory:
|
|
|
|
00010000 <-- Point A
|
|
00000000
|
|
00000000
|
|
00010000 <-- Point B
|
|
|
|
Now consider the following little piece of code:
|
|
|
|
LDA #00
|
|
EOR A
|
|
STA A
|
|
EOR A+1
|
|
STA A+1
|
|
EOR A+2
|
|
STA A+2
|
|
EOR A+3 ;point B
|
|
|
|
The result is:
|
|
|
|
00010000
|
|
00010000
|
|
00010000
|
|
00010000
|
|
|
|
This is the conceptual idea behind an EOR-buffer. Pretty neat, eh? But we
|
|
can't just implement this as-is. In fact we have a whole slew of things to
|
|
worry about now. Try EORing a vertical line. What about when two lines
|
|
share a single pixel at their intersection? What happens in color?
|
|
|
|
Ah reckon y'all will just have to wait until next time tuh see :).
|
|
|
|
|
|
Da Program
|
|
----------
|
|
|
|
Let's review the code. We check to see if we should increase or decrease the
|
|
rotation rate (or quit), and then update the angles. Next we calculate the
|
|
rotation matrix using a table of sines and cosines. Then we rotate and
|
|
project the points by using a table of d/(z/64-z0) values. Somewhere in there
|
|
we clear a working buffer, draw all of the lines, swap the buffers, pass Go,
|
|
collect $200 (actually, considering where the buffers are located we either
|
|
collect $300 or $380 :), and go around the loop again.
|
|
|
|
First, some bugs. There were two places in the line drawing routine where an
|
|
SBC was performed with the carry clear when it should have been set, so we
|
|
need to add some SECs in there. Somewhere there is a strange bug related to
|
|
y-rotations, but I didn't track it down.
|
|
|
|
Although not a bug, there is something to think about. On the computer, x
|
|
increases to the right, and y-increases downwards, with z coming out of the
|
|
screen. But this is a left-handed coordinate system, and all our
|
|
calculations were performed in a right-handed coordinate system. What this
|
|
means is that one of our coordinates is actually a mirror-image of what it
|
|
should be, while the other coordinate is where it is supposed to be.
|
|
Remember that a projection generates a negative mirror-image of the object --
|
|
the computer coordinate system mirrors a single axis of the image again!
|
|
|
|
Because of the symmetry of a cube, this makes no difference. A smart way to
|
|
fix this is to translate the object in _front_ of the projection plane, i.e.
|
|
to use the translation z=z+c instead of the currently used z=z-c, but still
|
|
project through the origin and into the plane z=1. Since I am not
|
|
particularly smart though, not to mention lazy and unmotivated, I didn't
|
|
bother to fix this.
|
|
|
|
Before we start adding the new stuff like hidden surfaces into the code, why
|
|
don't we think about doing some simple optimizations to the old code? One
|
|
really easy thing to fix is in the projection routine. You will recall that
|
|
the earlier program rotated z and then added 128 to it to use as an index.
|
|
Why bother to add 128 at all? I dunno -- sometimes things seem like a good
|
|
idea at the time. So that's something to fix. It's not that it's a big waste
|
|
of time, it's just one of those annoying things that there's no reason for.
|
|
|
|
How about the variables? They're all just sitting at the end of the program
|
|
-- why not move them all into zero page? Sounds good to me! We just need to
|
|
make sure we don't use any sensitive locations in zero page that will hose
|
|
the whole computer. So now that's fixed.
|
|
|
|
On the C64 an interrupt is performed every 60th of a second which scans the
|
|
keyboard and things like that -- why in the world do we want that running in
|
|
the middle of all our calculations? I dunno -- let's turn it off (but turn
|
|
it back on before checking to see if F1 etc. was pressed!).
|
|
|
|
A footnote observation: when the rotation matrix is calculated, two macros
|
|
are used (MUL2 and DIV2) which multiply and divide a signed number by two.
|
|
It never ceases to amaze me what happens when you simply sit down and think
|
|
about something, and in this case these two macros can be made much simpler:
|
|
|
|
MUL2 ASL ;That's all, folks
|
|
|
|
DIV2 CLC
|
|
BPL :POS
|
|
SEC
|
|
:POS ROR
|
|
|
|
These two routines will multiply/divide a signed 2's complement number
|
|
by two (note that the source included with this article uses the old method).
|
|
|
|
There's the easy stuff to fix. What about the calculations themselves? The
|
|
rotation is pretty straightforward -- nah, skip that. The line drawing
|
|
routine takes up an awful lot of time -- maybe we can speed that up? That's
|
|
for a future article :). Clearing the buffer takes a lot of time, but now
|
|
that we're going to have filled faces there isn't too much we can do about
|
|
that. In fact, so much more time is spent in those two areas than is spent
|
|
in other parts of the code that any other optimizations we make really aren't
|
|
going to make a very big difference... BUT...
|
|
|
|
How about multiplications?
|
|
|
|
|
|
Fast Signed Multiply
|
|
--------------------
|
|
|
|
Ah, now here is something we can fix. Consider the following function:
|
|
|
|
f(x) = x*x/4
|
|
|
|
Now notice that
|
|
|
|
f(a+b) - f(a-b) = a*b
|
|
|
|
Wowsers! All we need to do is have a table of squares, and we can do a
|
|
multiplication in no time!
|
|
|
|
Whoa there, wait a minute, all of our calculations are using signed numbers.
|
|
Won't that make a difference? Well, the above calculation is completely
|
|
general -- I never said what the sign of a and b are. The fact that we are
|
|
using two's complement notation makes this even simpler!
|
|
|
|
Recall that our multiplication is
|
|
|
|
x -> x * [d/(z0-z/64)]
|
|
|
|
where x is a signed floating point number multiplied by 64 (that is, instead
|
|
of going from -1 to 1 x would go from -64 to 64). Previously we made d
|
|
large, so that the table of d/(z-z0) would be more accurate. Then we
|
|
multiplied the numbers together and divided by 64, a procedure which took
|
|
between 150 and 180 cycles, leaving us with a signed, 8-bit result.
|
|
|
|
Well, that's easy to duplicate. From our first equation above, we see that
|
|
|
|
(a*b)/64 = [ f(a+b) - f(a-b) ]/64
|
|
= g(a+b) - g(a-b)
|
|
where
|
|
g(x) = x*x/256.
|
|
|
|
In other words, if we modify our table slightly, we get exactly the result we
|
|
want. So here is the code to multiply two numbers together:
|
|
|
|
* A*Y -> A Signed, 8-bit result
|
|
|
|
STA ZP1 ;ZP1 -- zero page pointer to table of g(x)
|
|
EOR #$FF
|
|
CLC
|
|
ADC #$01
|
|
STA ZP2 ;ZP2 also points to g(x)
|
|
LDA (ZP1),Y ;g(Y+A)
|
|
SEC
|
|
SBC (ZP2),Y ;g(Y-A)
|
|
|
|
And that's it -- we're done. The above takes 24-26 cycles to execute -- not
|
|
bad at all! Yes, with another table we could make it even faster, but this
|
|
is good enough for us.
|
|
|
|
At the moment we don't do very many multiplications, but in the future, when
|
|
we write a generalized routine to rotate and project an arbitrary object,
|
|
this will give us a humongous time savings.
|
|
|
|
Astute readers may be thinking ahead here: in the program, for each
|
|
projection we have two multiplications, x=x*c and y=y*c, where c is the same
|
|
in both cases. So if we store c in ZP1 and ZP2, we can make the
|
|
multiplication even more efficient, right? The answer is yes, but only by
|
|
being extremely careful, for reasons that will be detailed in exactly two
|
|
paragraphs.
|
|
|
|
BUT WAIT! We have to think about a few things here. What happens when we
|
|
multiply, say, -1 * -1. In two's complement notation -1 is equal to 255. So
|
|
our above algorithm adds 255 to 255 to get an index into the table and
|
|
gets... oops! Our table needs to be larger than 256 bytes! In fact this is
|
|
very easy to fix, because of the way two's complement works. All we need to
|
|
do is put an exact copy of the 256 byte table on top of itself, and the table
|
|
will work fine. (If you look at the initialization program you will notice
|
|
that the statement is: q%=s*s:poke bm+j,q%:poke bm+j+256,q%).
|
|
|
|
BUT WAIT!!! What kinds of numbers are we multiplying together here? Our
|
|
vertices start out at the points (+/-1,+/-1,+/-1). Our rotations correspond
|
|
to moving these points around on a sphere, so it is easy to see that the
|
|
largest rotated value we can have is sqr(3), the radius of this sphere.
|
|
Since we are multiplying these numbers by 64, the largest value we can have
|
|
for these numbers is 64*sqr(3) = 111. Okay, no big whoop, what are the
|
|
values for d/(z0-z/64) anyways? Well, for z0=5 and d=150 say we get values
|
|
like 28...
|
|
|
|
ACK! When we go to multiply we are going to add 111 to 28 and get 137, but
|
|
in two's complement notation this is equal to -119.
|
|
|
|
Example:
|
|
a=28
|
|
b=111
|
|
f(b+a) = f(137) = f(-119) in two's-complement notation
|
|
f(b-a) = f(83)
|
|
|
|
In our table lookup we really want 137*137 but we are going to get -119*-119!
|
|
One option is to never choose d very large so that we don't have this
|
|
problem, but the solution turns out to be much simpler, again due to the way
|
|
two's complementing works.
|
|
|
|
We can see that we can get numbers larger than 127 when we add the two
|
|
multiplicands together. What is the _smallest_ number we will come up with?
|
|
Certainly the smallest x is going to get is -111. Ahhh... d/(z0-z/64) is
|
|
always positive, so when we add them together we will get something larger
|
|
than -111, which in two's complement notation is 145. This means that we can
|
|
treat the table entries between 127 and at least 145 as positive numbers
|
|
instead of two's complement negative numbers, and everything will work out
|
|
great.
|
|
|
|
Incidentally, fudging this table provides an easy way to add pretty cool
|
|
special effects. The initialization program sets up the math table using the
|
|
following line:
|
|
|
|
[for j=0 to 255]
|
|
290 S=J:IF S>150 THEN S=256-S
|
|
[poke bm+j,S*S]
|
|
|
|
Try changing the inequality from S>150 to S>120 (or S>127, where it would be
|
|
for a two's complement table), and see what happens!
|
|
|
|
And this is why we can't store d/(z0-z) in the pointers ZP1 and ZP2 -- if we
|
|
did, then for a given multiplication we could get numbers larger than 127 and
|
|
smaller than -128, and our clever table would no longer work right. We can
|
|
still get around this -- all we need is two clever tables, one for the
|
|
positive d/(z0-z) and one for negative d/(z0-z). For the first table, we
|
|
have the situation outlined above: no numbers smaller than -90 or so, and
|
|
numbers possible larger than 127. For the second table we have the reverse
|
|
situation: no numbers larger than 90 or so, but possible numbers less than
|
|
-128. Since we are using two pointers anyways (ZP1 and ZP2), this is not
|
|
difficult to implement.
|
|
|
|
The end result is that you can do the entire projection in around 36 cycles
|
|
if you so desire. 36 cycles? Note that for the second table the code does
|
|
something like EOR #$FF; CLC; ADC #$01. Well, if we set up the second table
|
|
as f(x)=(x+1)^2/4 then we have included the CLC and ADC #$01 into the table,
|
|
so the instructions can be removed. The entire projection routine is then:
|
|
|
|
... (rotate z)
|
|
STA ZP1
|
|
EOR #$FF
|
|
STA ZP2
|
|
... (rotate x)
|
|
TAY
|
|
LDA (ZP1),Y
|
|
SEC
|
|
SBC (ZP2),Y ;Now A contains projected X
|
|
... (rotate y)
|
|
TAY
|
|
LDA (ZP1),Y
|
|
SEC
|
|
SBC (ZP2),Y
|
|
|
|
Looks like 36-40 cycles to me! The program doesn't implement this -- it only
|
|
uses a single table, and repeats the STA ZP1 stuff at each step. A few
|
|
cycles wasted won't kill us (there are plenty of cycles wasted in the code),
|
|
and it is probably tricky enough to follow as it is.
|
|
|
|
You might be asking, what is the true minimum value for a given z0 and d?
|
|
Well, I tried writing down a set of equations and minimizing according to
|
|
some constraints, and I ended up with a sixth-order polynomial which I had to
|
|
write little newton-iteration solver for. In other words, I don't think you
|
|
can write down a simple function of d and z0 to give the table boundaries. I
|
|
found 150 to be a perfectly reasonable number to use.
|
|
|
|
Incidentally, this is why the projection was not changed to z=z+c -- I didn't
|
|
want to go fiddling around with it again. Maybe next time :).
|
|
|
|
ONE MORE THING!!! This is very important. The math table MUST be on an even
|
|
boundary for the above algorithm to work correctly. This one is easy to get
|
|
bit by.
|
|
|
|
Logarithmic Multiplication
|
|
--------------------------
|
|
|
|
As long as we're talking about fast multiplication here, it is worthwhile to
|
|
mention another method for multiplying two numbers together. To understand
|
|
it you need to understand two important properties of logarithms:
|
|
|
|
log(x*y) = log(x) + log(y)
|
|
log_b(x) = y <=> b^y = x
|
|
|
|
These are a reflection of the fact that logarithms are inverses of
|
|
exponentiation. So you can see that another way to multiply two numbers
|
|
together is to take their logs, add, and then exponentiate the result. So
|
|
you could have a table of log_2 (base 2 logarithms) and another table of 2^x,
|
|
and do a multiplication very quickly. (Actually, you'd want a table of
|
|
32*log_2(x), since log_2(256)=8). Why wasn't this method used?
|
|
|
|
First, dealing with signed numbers is much trickier -- the logarithm of a
|
|
negative number is a complex (i.e. real+imaginary) number, complete with
|
|
branch cuts. You can get around this by setting up the tables in a special
|
|
way (for instance by letting log(-x)=-log(x)) and putting in some special
|
|
handling, but it isn't as efficient as the algorithm used in the program.
|
|
|
|
Second, accuracy decreases significantly as x and y get large, so that for an
|
|
eight-bit table of logarithms you will often get an answer that is off by one
|
|
or more. You can in fact get around this problem by using some sneaky
|
|
manipulation -- if you are interested in seeing this, contact us!
|
|
|
|
But it is worthwhile to keep this method in mind if you need a really fast
|
|
multiplication and you aren't too worried about accuracy.
|
|
|
|
Christopher Jam (phillips@ee.uwa.edu.au) has come up with an interesting
|
|
variation on this method. It calculates 64+64*x/z and uses a slightly
|
|
different structure for the signed numbers, and runs almost as fast as the
|
|
method used by the program -- contact him for more information if you're
|
|
interested.
|
|
|
|
|
|
Hidden Surfaces
|
|
---------------
|
|
|
|
The remainder of this follows right from the discussion section. In the
|
|
program the cube vertices are labeled as
|
|
|
|
P1 = 1,1,1
|
|
P2 = 1,-1,1
|
|
P3 = -1,-1,1
|
|
P4 = -1,1,1
|
|
P5 = 1,1,-1
|
|
P6 = 1,-1,-1
|
|
P7 = -1,-1,-1
|
|
P8 = -1,1,-1
|
|
|
|
and the faces are chosen to be
|
|
|
|
Face 1: P1 P2 P3 P4
|
|
6: P5 P6 P7 P8
|
|
Face 2: P1 P2 P5 P6
|
|
5: P3 P4 P7 P8
|
|
Face 3: P1 P4 P8 P5
|
|
4: P2 P3 P6 P7
|
|
|
|
(think of it as a six-sided dice, with six opposite of one, etc.). The normal
|
|
vectors are then
|
|
|
|
Face 1: P1-P5
|
|
Face 2: P1-P4
|
|
Face 3: P1-P2
|
|
|
|
This means that we need to store the z-coordinates for points 1,2,4, and 5.
|
|
Note that the opposite faces have exactly opposite normal vectors, so that
|
|
for instance the normal vector for face 6 is P5-P1, the negative of face 1.
|
|
|
|
Here is something to consider: when one face is visible, the opposite face
|
|
cannot be visible! Because of the way projections work, though, the converse
|
|
is not true; it is entirely possible to have two opposite faces invisible.
|
|
To prove this to yourself just look at your favorite box, like your monitor,
|
|
straight-on, and notice that you can't see the sides!
|
|
|
|
All that the program does is subtract z-coordinates and add them to the
|
|
constant K, and check the sign. Unfortunately we can have a positive
|
|
overflow while adding stuff together (since these are signed numbers), and if
|
|
we don't catch the positive overflow we will calculate a negative result when
|
|
the result is actually positive! This will of course wreck the hidden
|
|
surface removal.
|
|
|
|
|
|
Filled Faces
|
|
------------
|
|
|
|
The program currently uses the first algorithm to fill faces, i.e. the
|
|
cookie-cutter elephant-carving method. During the projections the program
|
|
checks each value of y to find the minimum and maximum values for this plot,
|
|
ymin and ymax. The program then clears the buffer up to and including ymin,
|
|
fills the buffer from ymin+1 to ymax-1, and then clears the rest of the
|
|
buffer. Why does it clear ymin and ymax? Because the only thing that can
|
|
happen on those lines is an edge -- there is no point in filling these lines
|
|
and then clearing them, since they will always be clear. By only filling the
|
|
buffer between ymin and ymax, we save some time in removing the junk from the
|
|
edges of the cube.
|
|
|
|
Next, the cube is drawn. The background is black and the faces are white,
|
|
i.e. our fill color is white. Clearly then we want to draw our lines in
|
|
black. I could have reversed background and foreground colors and left the
|
|
line routine as-is, but of course being the lazy programmer I am I decided
|
|
instead to change the table BITP. You may recall that the earlier table had
|
|
entries like %10000000 %01000000 etc. Now it has entries like %01111111
|
|
%10111111 etc., and instead of ORAing the values into the buffer, they are
|
|
ANDed into the buffer. This then draws lines of zeroes into our buffer which
|
|
is solid ones.
|
|
|
|
Finally, to un-fill the outside of the cube the program simply goes through
|
|
the buffer from ymin to ymax, coloring everything black until it hits a zero,
|
|
i.e. an edge. At this point it calculates the appropriate pattern to clear
|
|
up to the edge, and then does the same thing starting from the right hand
|
|
side of the buffer. In other words it runs along a specific y-value coloring
|
|
everything black until it hits the edge of the cube, and does this for all
|
|
the relevant y-values.
|
|
|
|
|
|
Texture Mapping
|
|
---------------
|
|
|
|
More of a fill-pattern really. The program cube3d2.1.o does all of the above
|
|
but in multicolor mode. Now instead of using a solid color to fill the
|
|
buffer the program uses a series of colored lines -- really a very simple
|
|
pattern. A much neater thing would be to have a pattern drawn out in a
|
|
pattern buffer, and to copy that into the drawing buffer. Other things to
|
|
try are colored squares which shift around. cube3d2.1.o is just a really
|
|
quick hack, but at least it demonstrates the concept.
|
|
|
|
MAKE SURE that you change the value of D from 170 to 85 if you try this
|
|
program! Pixels are doubled now, so that resolution is cut in half. This is
|
|
located at line 240 in INIT3D2.0
|
|
|
|
|
|
Memory Map
|
|
----------
|
|
|
|
The main program is located at $8000=32768 and is 3200 bytes long.
|
|
|
|
$8000-$8C00 - Program
|
|
$8C00-$8C80 - Bit position table
|
|
$8C80-$8D00 - Table of sines
|
|
$8D00-$8D80 - Table of cosines.
|
|
$8D80-$8E80 - Table of d/(z0-z/64)
|
|
$8F00-$9100 - Two 256-byte tables of g(x)=x*x/256
|
|
|
|
$3000 - First drawing buffer
|
|
$3800 - Second drawing buffer
|
|
|
|
INIT3D is a simple basic program to set up the tables. For INIT3D2.x the
|
|
important setup routines are:
|
|
|
|
lines 100-150 - Set up the trigonometric tables
|
|
lines 233-310 - Set up the projection and mult tables
|
|
240 - Location of constants D and Z0
|
|
290 - Set table boundary for multiplication
|
|
|
|
|
|
That's all -- until next time...
|
|
|
|
Steve Judd George Taylor 12/2/95
|
|
|
|
This document is Copyright 1995 by Stephen Judd and George Taylor. Much like
|
|
the previous one. It is also freely distributable.
|
|
|
|
And here is the source code:
|
|
|
|
|
|
********************************
|
|
* *
|
|
* Stephen Judd *
|
|
* George Taylor *
|
|
* Started: 7/11/94 *
|
|
* Finished: 7/19/94 *
|
|
* v2.0 Completed: 12/17/94 *
|
|
* *
|
|
* Well, if all goes well this *
|
|
* program will rotate a cube. *
|
|
* *
|
|
* v2.0 + New and Improved! *
|
|
* Now with faster routines, *
|
|
* hidden surfaces, filled *
|
|
* faces, and extra top secret *
|
|
* text messages! *
|
|
* *
|
|
* This program is intended to *
|
|
* accompany the article in *
|
|
* C=Hacking, Jan. 95 issue. *
|
|
* For details on this program, *
|
|
* read the article! *
|
|
* *
|
|
* Write to us! *
|
|
* *
|
|
* Myself when young did *
|
|
* eagerly frequent *
|
|
* Doctor and Saint, and heard *
|
|
* great Argument *
|
|
* About it and about: but *
|
|
* evermore *
|
|
* Came out by the same Door *
|
|
* as in I went. *
|
|
* - Rubaiyat *
|
|
* *
|
|
* Though I speak with the *
|
|
* tongues of men and of angles *
|
|
* and have not love, I am *
|
|
* become as sounding brass, or *
|
|
* a tinkling cymbal. *
|
|
* - 1 Corinthians 13 *
|
|
* *
|
|
* P.S. This was written using *
|
|
* Merlin 128. *
|
|
********************************
|
|
|
|
ORG $8000
|
|
|
|
* Constants
|
|
|
|
BUFF1 EQU $3000 ;First character set
|
|
BUFF2 EQU $3800 ;Second character set
|
|
BUFFER EQU $A3 ;Presumably the tape won't be running
|
|
X1 EQU $FB ;Points for drawing a line
|
|
Y1 EQU $FC ;These zero page addresses
|
|
X2 EQU $FD ;don't conflict with BASIC
|
|
Y2 EQU $FE
|
|
DX EQU $F9
|
|
DY EQU $FA
|
|
TEMP1 EQU $FB ;Of course, could conflict with x1
|
|
TEMP2 EQU $FC ;Temporary variables
|
|
ZTEMP EQU $02 ;Used for buffer swap. Don't touch.
|
|
Z1 EQU $22 ;Used by math routine
|
|
Z2 EQU $24 ;Don't touch these either!
|
|
K EQU $B6 ;Constant used for hidden
|
|
;surface detection - don't touch
|
|
FACES EQU $B5 ;Used in hidden surfaces.
|
|
YMIN EQU $F7 ;Used in filled faces -- as
|
|
YMAX EQU $F8 ;usual, don't touch
|
|
ANGMAX EQU 120 ;There are 2*pi/angmax angles
|
|
|
|
* VIC
|
|
|
|
VMCSB EQU $D018
|
|
BKGND EQU $D020
|
|
BORDER EQU $D021
|
|
SSTART EQU 1344 ;row 9 in screen memory at 1024
|
|
|
|
|
|
* Kernal
|
|
|
|
CHROUT EQU $FFD2
|
|
GETIN EQU $FFE4
|
|
|
|
* Some variables
|
|
|
|
TX1 = $3F
|
|
TY1 = $40
|
|
TX2 = $41
|
|
TY2 = $42
|
|
P1X = $92 ;These are temporary storage
|
|
P1Y = $93 ;Used in plotting the projection
|
|
P2X = $94
|
|
P2Y = $95 ;They are here so that we
|
|
P3X = $96 ;don't have to recalculate them.
|
|
P3Y = $AE
|
|
P4X = $AF ;They make life easy.
|
|
P4Y = $B0
|
|
P5X = $B1 ;Why are you looking at me like that?
|
|
P5Y = $B2 ;Don't you trust me?
|
|
P6X = $B3
|
|
P6Y = $B4 ;Having another child wasn't my idea.
|
|
P7X = $71
|
|
P7Y = $50
|
|
P8X = $51
|
|
P8Y = $52
|
|
P1Z = $57 ;These are z-coordinates
|
|
P2Z = $58 ;We only need these four to check
|
|
P4Z = $59 ;for hidden faces
|
|
P5Z = $60
|
|
DSX = $61 ;DSX is the increment for
|
|
;rotating around x
|
|
DSY = $62 ;Similar for DSY, DSZ
|
|
DSZ = $63
|
|
SX = $64 ;These are the actual angles in x y and z
|
|
SY = $65
|
|
SZ = $66
|
|
T1 = $67 ;These are used in the rotation
|
|
T2 = $68
|
|
T3 = $69 ;See the article for more details
|
|
T4 = $6A
|
|
T5 = $6B
|
|
T6 = $6C
|
|
T7 = $6D
|
|
T8 = $6E
|
|
T9 = $6F
|
|
T10 = $70
|
|
A11 = $A5 ;These are the elements of the rotation matrix
|
|
B12 = $A6 ;XYZ
|
|
C13 = $A7
|
|
D21 = $A8 ;The number denotes (row,column)
|
|
E22 = $A9
|
|
F23 = $AA
|
|
G31 = $AB
|
|
H32 = $AC
|
|
I33 = $AD
|
|
|
|
|
|
*** Macros
|
|
|
|
MOVE MAC
|
|
LDA ]1
|
|
STA ]2
|
|
<<<
|
|
|
|
GETKEY MAC ;Wait for a keypress
|
|
WAIT JSR GETIN
|
|
CMP #00
|
|
BEQ WAIT
|
|
<<<
|
|
|
|
*-------------------------------
|
|
|
|
LDA #$00
|
|
STA BKGND
|
|
STA BORDER
|
|
LDA VMCSB
|
|
AND #%00001111 ;Screen memory to 1024
|
|
ORA #%00010000
|
|
STA VMCSB
|
|
|
|
LDY #00
|
|
LDA #<TTEXT
|
|
STA TEMP1
|
|
LDA #>TTEXT
|
|
STA TEMP2
|
|
JMP TITLE
|
|
TTEXT HEX 9305111111 ;clear screen, white, crsr dn
|
|
TXT ' cube3d v2.0',0d,0d
|
|
TXT ' by',0d
|
|
HEX 9F ;cyan
|
|
TXT ' stephen judd'
|
|
HEX 99
|
|
TXT ' george taylor',0d,0d
|
|
HEX 9B
|
|
TXT ' check out the jan. 95 issue of',0d
|
|
HEX 96
|
|
TXT ' c=hacking'
|
|
HEX 9B
|
|
TXT ' for more details!',0d
|
|
HEX 0D1D1D9E12
|
|
TXT 'f1/f2',92
|
|
TXT ' - inc/dec x-rotation',0d
|
|
HEX 1D1D12
|
|
TXT 'f3/f4',92
|
|
TXT ' - inc/dec y-rotation',0d
|
|
HEX 1D1D12
|
|
TXT 'f5/f6',92
|
|
TXT ' - inc/dec z-rotation',0d
|
|
HEX 1D1D12
|
|
TXT 'f7',92
|
|
TXT ' resets',0d
|
|
TXT ' press q to quit',0d
|
|
HEX 0D05
|
|
TXT ' press any key to begin',0d
|
|
HEX 00
|
|
TITLE LDA (TEMP1),Y
|
|
BEQ :CONT
|
|
JSR CHROUT
|
|
INY
|
|
BNE TITLE
|
|
INC TEMP2
|
|
JMP TITLE
|
|
TXT 'This is a secret text message!'
|
|
:CONT >>> GETKEY
|
|
|
|
**** Set up tables(?)
|
|
|
|
* Tables are currently set up in BASIC
|
|
* and by the assembler.
|
|
|
|
TABLES LDA #>TMATH
|
|
STA Z1+1
|
|
STA Z2+1
|
|
|
|
**** Clear screen and set up "bitmap"
|
|
SETUP LDA #$01 ;White
|
|
STA $D021 ;This is done so that older
|
|
LDA #147 ;machines will set up
|
|
JSR CHROUT
|
|
LDA #$00 ;correctly
|
|
STA $D021
|
|
LDA #<SSTART
|
|
ADC #12 ;The goal is to center the graphics
|
|
STA TEMP1 ;Column 12
|
|
LDA #>SSTART ;Row 9
|
|
STA TEMP1+1 ;SSTART points to row 9
|
|
LDA #00
|
|
LDY #00
|
|
LDX #00 ;x will count 16 rows for us
|
|
CLC
|
|
|
|
:LOOP STA (TEMP1),Y
|
|
INY
|
|
ADC #16
|
|
BCC :LOOP
|
|
CLC
|
|
LDA TEMP1
|
|
ADC #40 ;Need to add 40 to the base pointer
|
|
STA TEMP1 ;To jump to the next row
|
|
LDA TEMP1+1
|
|
ADC #00 ;Take care of carries
|
|
STA TEMP1+1
|
|
LDY #00
|
|
INX
|
|
TXA ;X is also an index into the character number
|
|
CPX #16
|
|
BNE :LOOP ;Need to do it 16 times
|
|
|
|
**** Set up buffers
|
|
|
|
LDA #<BUFF1
|
|
STA BUFFER
|
|
LDA #>BUFF1
|
|
STA BUFFER+1
|
|
STA ZTEMP ;ztemp will make life simple for us
|
|
LDA VMCSB
|
|
AND #%11110001 ;Start here so that swap buffers will work right
|
|
ORA #%00001110
|
|
STA VMCSB
|
|
|
|
**** Set up initial values
|
|
|
|
INIT LDA #00
|
|
STA DSX
|
|
STA DSY
|
|
STA DSZ
|
|
STA SX
|
|
STA SY
|
|
STA SZ
|
|
|
|
*-------------------------------
|
|
* Main loop
|
|
|
|
**** Get keypress
|
|
|
|
MAIN
|
|
CLI
|
|
KPRESS JSR GETIN
|
|
CMP #133 ;F1?
|
|
BNE :F2
|
|
LDA DSX
|
|
CMP #ANGMAX/2 ;No more than pi
|
|
BEQ :CONT
|
|
INC DSX ;otherwise increase x-rotation
|
|
JMP :CONT
|
|
:F2 CMP #137 ;F2?
|
|
BNE :F3
|
|
LDA DSX
|
|
BEQ :CONT
|
|
DEC DSX
|
|
JMP :CONT
|
|
:F3 CMP #134
|
|
BNE :F4
|
|
LDA DSY
|
|
CMP #ANGMAX/2
|
|
BEQ :CONT
|
|
INC DSY ;Increase y-rotation
|
|
JMP :CONT
|
|
:F4 CMP #138
|
|
BNE :F5
|
|
LDA DSY
|
|
BEQ :CONT
|
|
DEC DSY
|
|
JMP :CONT
|
|
:F5 CMP #135
|
|
BNE :F6
|
|
LDA DSZ
|
|
CMP #ANGMAX/2
|
|
BEQ :CONT
|
|
INC DSZ ;z-rotation
|
|
JMP :CONT
|
|
:F6 CMP #139
|
|
BNE :F7
|
|
LDA DSZ
|
|
BEQ :CONT
|
|
DEC DSZ
|
|
JMP :CONT
|
|
:F7 CMP #136
|
|
BNE :Q
|
|
JMP INIT
|
|
:Q CMP #'q' ;q quits
|
|
BNE :CONT
|
|
JMP CLEANUP
|
|
|
|
:CONT SEI ;Speed things up a bit
|
|
|
|
**** Update angles
|
|
|
|
UPDATE CLC
|
|
LDA SX
|
|
ADC DSX
|
|
CMP #ANGMAX ;Are we >= maximum angle?
|
|
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, calculate t1,t2,...,t10
|
|
|
|
** Two macros to simplify our life
|
|
ADDA MAC ;Add two angles together
|
|
CLC
|
|
LDA ]1
|
|
ADC ]2
|
|
* Use two trig tables to remove the below CMP etc. code
|
|
CMP #ANGMAX ;Is the sum > 2*pi?
|
|
BCC DONE
|
|
SBC #ANGMAX ;If so, subtract 2*pi
|
|
DONE <<<
|
|
|
|
SUBA MAC ;Subtract two angles
|
|
SEC
|
|
LDA ]1
|
|
SBC ]2
|
|
BCS DONE
|
|
ADC #ANGMAX ;Oops, we need to add 2*pi
|
|
DONE <<<
|
|
|
|
** Now calculate t1,t2,etc.
|
|
|
|
>>> SUBA,SY ;SZ
|
|
STA T1 ;t1=sy-sz
|
|
>>> ADDA,SY ;SZ
|
|
STA T2 ;t2=sy+sz
|
|
>>> ADDA,SX ;SZ
|
|
STA T3 ;t3=sx+sz
|
|
>>> SUBA,SX ;SZ
|
|
STA T4 ;t4=sx-sz
|
|
>>> ADDA,SX ;T2
|
|
STA T5 ;t5=sx+t2
|
|
>>> SUBA,SX ;T1
|
|
STA T6 ;t6=sx-t1
|
|
>>> ADDA,SX ;T1
|
|
STA T7 ;t7=sx+t1
|
|
>>> SUBA,T2 ;SX
|
|
STA T8 ;t8=t2-sx
|
|
>>> SUBA,SY ;SX
|
|
STA T9 ;t9=sy-sx
|
|
>>> ADDA,SX ;SY
|
|
STA T10 ;t10=sx+sy
|
|
|
|
* Et voila!
|
|
|
|
*** Next, calculate A,B,C,...,I
|
|
|
|
** Another useful little macro
|
|
DIV2 MAC ;Divide a signed number by 2
|
|
;It is assumed that the number
|
|
BPL POS ;is in the accumulator
|
|
CLC
|
|
EOR #$FF ;We need to un-negative the number
|
|
ADC #01 ;by taking it's complement
|
|
LSR ;divide by two
|
|
CLC
|
|
EOR #$FF
|
|
ADC #01 ;Make it negative again
|
|
JMP DONEDIV
|
|
POS LSR ;Number is positive
|
|
DONEDIV <<<
|
|
|
|
MUL2 MAC ;Multiply a signed number by 2
|
|
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 ;A=(cos(t1)+cos(t2))/2
|
|
:CALCB LDX T1
|
|
LDA SIN,X
|
|
SEC
|
|
LDX T2
|
|
SBC SIN,X
|
|
STA B12 ;B=(sin(t1)-sin(t2))/2
|
|
:CALCC LDX SY
|
|
LDA SIN,X
|
|
>>> MUL2
|
|
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 ;Di=(cos(t8)-cos(t7)+cos(t6)-cos(t5))/2
|
|
>>> DIV2
|
|
CLC
|
|
LDX T3
|
|
ADC SIN,X
|
|
SEC
|
|
LDX T4
|
|
SBC SIN,X
|
|
STA D21 ;D=(sin(t3)-sin(t4)+Di)/2
|
|
:CALCE SEC
|
|
LDX T5
|
|
LDA SIN,X
|
|
LDX T6
|
|
SBC SIN,X
|
|
SEC
|
|
LDX T7
|
|
SBC SIN,X
|
|
SEC
|
|
LDX T8
|
|
SBC SIN,X ;Ei=(sin(t5)-sin(t6)-sin(t7)-sin(t8))/2
|
|
>>> DIV2
|
|
CLC
|
|
LDX T3
|
|
ADC COS,X
|
|
CLC
|
|
LDX T4
|
|
ADC COS,X
|
|
STA E22 ;E=(cos(t3)+cos(t4)+Ei)/2
|
|
:CALCF LDX T9
|
|
LDA SIN,X
|
|
SEC
|
|
LDX T10
|
|
SBC SIN,X
|
|
STA F23 ;F=(sin(t9)-sin(t10))/2
|
|
:CALCG LDX T6
|
|
LDA SIN,X
|
|
SEC
|
|
LDX T8
|
|
SBC SIN,X
|
|
SEC
|
|
LDX T7
|
|
SBC SIN,X
|
|
SEC
|
|
LDX T5
|
|
SBC SIN,X ;Gi=(sin(t6)-sin(t8)-sin(t7)-sin(t5))/2
|
|
>>> DIV2
|
|
CLC
|
|
LDX T4
|
|
ADC COS,X
|
|
SEC
|
|
LDX T3
|
|
SBC COS,X
|
|
STA G31 ;G=(cos(t4)-cos(t3)+Gi)/2
|
|
:CALCH CLC
|
|
LDX T6
|
|
LDA COS,X
|
|
LDX T7
|
|
ADC COS,X
|
|
SEC
|
|
LDX T5
|
|
SBC COS,X
|
|
SEC
|
|
LDX T8
|
|
SBC COS,X ;Hi=(cos(t6)+cos(t7)-cos(t5)-cos(t8))/2
|
|
>>> DIV2
|
|
CLC
|
|
LDX T3
|
|
ADC SIN,X
|
|
CLC
|
|
LDX T4
|
|
ADC SIN,X
|
|
STA H32 ;H=(sin(t3)+sin(t4)+Hi)/2
|
|
:WHEW CLC
|
|
LDX T9
|
|
LDA COS,X
|
|
LDX T10
|
|
ADC COS,X
|
|
STA I33 ;I=(cos(t9)+cos(t10))/2
|
|
|
|
** It's all downhill from here.
|
|
JMP DOWNHILL
|
|
TXT 'Gee Brain, what do you want to do '
|
|
TXT 'tonight?'
|
|
|
|
** Rotate, project, and store the points
|
|
DOWNHILL
|
|
|
|
* A neat macro
|
|
NEG MAC ;Change the sign of a two's complement
|
|
CLC
|
|
LDA ]1 ;number.
|
|
EOR #$FF
|
|
ADC #$01
|
|
<<<
|
|
|
|
*-------------------------------
|
|
* These macros replace the previous projection
|
|
* subroutine.
|
|
|
|
SMULT MAC ;Multiply two signed 8-bit
|
|
;numbers: A*Y/64 -> A
|
|
STA Z1
|
|
CLC
|
|
EOR #$FF
|
|
ADC #$01
|
|
STA Z2
|
|
LDA (Z1),Y
|
|
SEC
|
|
SBC (Z2),Y
|
|
<<< ;All done :)
|
|
|
|
|
|
ADDSUB MAC ;Add or subtract two numbers
|
|
;depending on first input
|
|
IF -=]1 ;If subtract
|
|
SEC ;then use this code
|
|
SBC ]2
|
|
ELSE ;otherwise use this code
|
|
CLC
|
|
ADC ]2
|
|
FIN
|
|
<<<
|
|
|
|
|
|
PROJECT MAC ;The actual projection routine
|
|
;two inputs are used (x,y)
|
|
;corresponding to (+/-1,+/-1)
|
|
;The third input is used to
|
|
;determine if the rotated
|
|
;z-coordinate should be
|
|
;stored, and if so where.
|
|
;The calling routine handles
|
|
;changing the sign of z.
|
|
|
|
LDA I33 ;Calculate rotated z:
|
|
>>> ADDSUB,]1 ;G31 ;Add or subtract x
|
|
>>> ADDSUB,]2 ;H32 ;Add or subtract y
|
|
IF P,]3 ;Do we need to store the point?
|
|
STA ]3 ;Then do so!
|
|
FIN
|
|
* EOR #128 ;We are going to take 128+z
|
|
TAX ;Now it is ready for indexing
|
|
LDA ZDIV,X ;Table of d/(z+z0)
|
|
TAY ;Y now contains projection
|
|
|
|
LDA C13 ;Now calculate rotated x
|
|
>>> ADDSUB,]1 ;A11
|
|
>>> ADDSUB,]2 ;B12
|
|
>>> SMULT ;Signed multiply A*Y/64->A
|
|
CLC
|
|
ADC #64 ;Offset the coordinate
|
|
TAX ;Now X is rotated x!
|
|
|
|
LDA F23 ;Now it's y's turn
|
|
>>> ADDSUB,]1 ;D21
|
|
>>> ADDSUB,]2 ;E22
|
|
>>> SMULT
|
|
CLC
|
|
ADC #64 ;Offset
|
|
CMP YMIN ;Figure out if it is a
|
|
BCS NOTMIN ;min or max value for y
|
|
STA YMIN
|
|
BCC NOTMAX ;This is used in calculating
|
|
NOTMIN CMP YMAX ;the filled faces
|
|
BCC NOTMAX
|
|
STA YMAX
|
|
NOTMAX TAY ;Not really necessary
|
|
|
|
<<< ;All done
|
|
|
|
|
|
LDA #64 ;Reset Ymin and Ymax
|
|
STA YMIN
|
|
STA YMAX
|
|
|
|
* P1=[1 1 1]
|
|
>>> PROJECT,1;1;P1Z ;Rotated z stored in P1Z
|
|
STX P1X
|
|
STY P1Y
|
|
* P2=[1 -1 1]
|
|
>>> PROJECT,1 ;-1;P2Z
|
|
STX P2X
|
|
STY P2Y
|
|
* P3=[-1 -1 1]
|
|
>>> PROJECT,-1;-1;NOPE ;Don't store z-value
|
|
STX P3X
|
|
STY P3Y
|
|
* P4=[-1 1 1]
|
|
>>> PROJECT,-1;1;P4Z
|
|
STX P4X
|
|
STY P4Y
|
|
* P8=[-1 1 -1]
|
|
>>> NEG,C13
|
|
STA C13
|
|
>>> NEG,F23
|
|
STA F23
|
|
>>> NEG,I33
|
|
STA I33
|
|
>>> PROJECT,-1;1;NOPE
|
|
STX P8X
|
|
STY P8Y
|
|
* P7=[-1 -1 -1]
|
|
>>> PROJECT,-1;-1;NOPE
|
|
STX P7X
|
|
STY P7Y
|
|
* P6=[1 -1 -1]
|
|
>>> PROJECT,1;-1;NOPE
|
|
STX P6X
|
|
STY P6Y
|
|
* P5=[1 1 -1]
|
|
>>> PROJECT,1;1;P5Z
|
|
STX P5X
|
|
STY P5Y
|
|
|
|
* A little macro
|
|
|
|
SETBUF MAC ;Put buffers where they can be hurt
|
|
LDA #00
|
|
STA BUFFER
|
|
LDA ZTEMP ;ztemp contains the high byte here
|
|
STA BUFFER+1
|
|
<<<
|
|
|
|
**** Clear buffer
|
|
|
|
* >>> SETBUF
|
|
*CLRBUF LDA #$00 ;Pretty straightforward,
|
|
* LDX #$08 ;I think
|
|
* LDY #$00
|
|
*:LOOP STA (BUFFER),Y
|
|
* INY
|
|
* BNE :LOOP
|
|
* INC BUFFER+1
|
|
* DEX
|
|
* BNE :LOOP
|
|
|
|
* This is the new and improved buffer clear
|
|
* routine for filled faces
|
|
|
|
>>> SETBUF
|
|
STA TEMP1+1 ;buffer2 will point to
|
|
LDA #$80 ;buffer+128
|
|
STA TEMP1 ;Makes life faster for us
|
|
FILCLR LDA #00
|
|
LDX #$08 ;We'll do it two at a time
|
|
LDY #$00
|
|
:LOOP1 STA (BUFFER),Y
|
|
STA (TEMP1),Y
|
|
INY
|
|
CPY YMIN
|
|
BNE :LOOP1
|
|
LDA #$FF ;Now load with fills
|
|
:LOOP2 STA (BUFFER),Y
|
|
STA (TEMP1),Y
|
|
INY
|
|
CPY YMAX
|
|
BCC :LOOP2
|
|
LDA #$00 ;Black out the rest
|
|
:LOOP3 STA (BUFFER),Y
|
|
STA (TEMP1),Y
|
|
INY
|
|
BPL :LOOP3 ;Until Y=128
|
|
LDY #00
|
|
INC BUFFER+1
|
|
INC TEMP1+1
|
|
DEX
|
|
BNE :LOOP1 ;Go all the way around
|
|
|
|
**** Now draw the lines.
|
|
**** But first check for hidden faces!
|
|
**** Remember: P1=[1 1 1] P2=[1 -1 1] P3=[-1 -1 1]
|
|
**** P4=[-1 1 1] P5=[1 1 -1] P6=[1 -1 -1] P7=[-1 -1 -1]
|
|
**** P8=[-1 1 -1]
|
|
|
|
LINES LDA #00
|
|
STA FACES ;Hidden face counter
|
|
:FACE1 LDA K
|
|
SEC
|
|
SBC P1Z
|
|
BVS :FACE6 ;Overflow already?
|
|
CLC
|
|
ADC P5Z ;Is k-v1z < 0?
|
|
;If not, face is invisible
|
|
BVC :DRAW1 ;But we might have overflow
|
|
LDA P5Z ;Was overflow pos or neg?
|
|
:DRAW1 BPL :FACE6 ;If pos then k-v1z > 0
|
|
|
|
LDA #$01 ;Otherwise, draw the
|
|
STA FACES ;face!
|
|
|
|
LDA P1X
|
|
STA TX1
|
|
LDA P1Y
|
|
STA TY1
|
|
LDA P2X
|
|
STA TX2
|
|
LDA P2Y
|
|
STA TY2
|
|
JSR DRAW ;P1-P2
|
|
|
|
LDA P3X
|
|
STA TX1
|
|
LDA P3Y
|
|
STA TY1
|
|
JSR DRAW ;P2-P3
|
|
|
|
LDA P4X
|
|
STA TX2
|
|
LDA P4Y
|
|
STA TY2
|
|
JSR DRAW ;P3-P4
|
|
|
|
LDA P1X
|
|
STA TX1
|
|
LDA P1Y
|
|
STA TY1
|
|
JSR DRAW ;P4-P1 Face 1 done.
|
|
JMP :FACE2 ;If one is visible, the other
|
|
;isn't.
|
|
:FACE6 LDA K
|
|
SEC
|
|
SBC P5Z
|
|
BVS :FACE2
|
|
CLC
|
|
ADC P1Z ;Now check if K-v6z < 0
|
|
BVC :DRAW6 ;Love that overflow
|
|
LDA P1Z
|
|
:DRAW6 BPL :FACE2 ;If not, go on
|
|
|
|
LDA #$20
|
|
STA FACES ;Otherwise, draw it
|
|
|
|
LDA P5X
|
|
STA TX2
|
|
LDA P5Y
|
|
STA TY2
|
|
LDA P6X
|
|
STA TX1
|
|
LDA P6Y
|
|
STA TY1
|
|
JSR DRAW ;P5-P6
|
|
|
|
LDA P7X
|
|
STA TX2
|
|
LDA P7Y
|
|
STA TY2
|
|
JSR DRAW ;P6-P7
|
|
|
|
LDA P8X
|
|
STA TX1
|
|
LDA P8Y
|
|
STA TY1
|
|
JSR DRAW ;P7-P8
|
|
|
|
LDA P5X
|
|
STA TX2
|
|
LDA P5Y
|
|
STA TY2
|
|
JSR DRAW ;P8-P5
|
|
|
|
:FACE2 LDA K
|
|
SEC
|
|
SBC P1Z
|
|
BVS :FACE5
|
|
CLC
|
|
ADC P4Z ;K-v2z < 0?
|
|
BVC :DRAW2
|
|
LDA P4Z
|
|
:DRAW2 BPL :FACE5
|
|
LDA #$02 ;If so, draw it!
|
|
ORA FACES
|
|
STA FACES
|
|
|
|
LDX P1X ;We're doing this this way
|
|
STX TX1 ;to save a few cycles
|
|
LDX P1Y
|
|
STX TY1
|
|
|
|
AND #$01 ;Shares an edge with face 1
|
|
BNE :F2S2 ;Skip to next edge if present
|
|
|
|
LDA P2X
|
|
STA TX2
|
|
LDA P2Y
|
|
STA TY2
|
|
JSR DRAW ;P1-P2
|
|
|
|
:F2S2 LDX P5X
|
|
STX TX2
|
|
LDX P5Y
|
|
STX TY2
|
|
JSR DRAW ;P1-P5
|
|
|
|
LDX P6X
|
|
STX TX1
|
|
LDX P6Y
|
|
STX TY1
|
|
|
|
LDA FACES
|
|
AND #$20 ;Also shares an edge with 6
|
|
BNE :F2S4
|
|
|
|
JSR DRAW ;P5-P6
|
|
|
|
:F2S4 LDA P2X
|
|
STA TX2
|
|
LDA P2Y
|
|
STA TY2 ;Such is face 2
|
|
JSR DRAW ;P6-P2
|
|
JMP :FACE3 ;Skip 5
|
|
|
|
:FACE5 LDA K
|
|
SEC
|
|
SBC P4Z
|
|
BVS :FACE3
|
|
CLC
|
|
ADC P1Z ;Same thing again...
|
|
BVC :DRAW5
|
|
LDA P1Z
|
|
:DRAW5 BPL :FACE3
|
|
LDA #$10
|
|
ORA FACES
|
|
STA FACES
|
|
|
|
LDX P3X
|
|
STX TX1
|
|
LDX P3Y
|
|
STX TY1
|
|
|
|
AND #$01 ;Shares with 1
|
|
BNE :F5S2
|
|
|
|
LDA P4X
|
|
STA TX2
|
|
LDA P4Y
|
|
STA TY2
|
|
JSR DRAW ;P3-P4
|
|
|
|
:F5S2 LDA P7X
|
|
STA TX2
|
|
LDA P7Y
|
|
STA TY2
|
|
JSR DRAW ;P3-P7
|
|
|
|
LDA P8X
|
|
STA TX1
|
|
LDA P8Y
|
|
STA TY1
|
|
|
|
LDA FACES
|
|
AND #$20 ;Shares with 6
|
|
BNE :F5S4
|
|
|
|
JSR DRAW ;P7-P8
|
|
:F5S4 LDA P4X
|
|
STA TX2
|
|
LDA P4Y
|
|
STA TY2 ;P8-P4
|
|
JSR DRAW ;Two more to go!
|
|
|
|
:FACE3 LDA K
|
|
SEC
|
|
SBC P1Z
|
|
BVS :FACE4
|
|
CLC
|
|
ADC P2Z
|
|
BVC :DRAW3
|
|
LDA P2Z
|
|
:DRAW3 BPL :FACE4 ;Ah reckon it's a'hidden, yup
|
|
LDA #$04
|
|
ORA FACES
|
|
STA FACES
|
|
|
|
LDX P1X
|
|
STX TX1
|
|
LDX P1Y
|
|
STX TY1
|
|
|
|
AND #$01 ;Shares with 1
|
|
BNE :F3S2
|
|
|
|
LDA P4X
|
|
STA TX2
|
|
LDA P4Y
|
|
STA TY2
|
|
JSR DRAW ;P1-P4
|
|
|
|
:F3S2 LDX P5X
|
|
STX TX2
|
|
LDX P5Y
|
|
STX TY2
|
|
|
|
LDA FACES
|
|
AND #$02 ;Shares with 2
|
|
BNE :F3S3
|
|
|
|
JSR DRAW ;P1-P5
|
|
:F3S3 LDX P8X
|
|
STX TX1
|
|
LDX P8Y
|
|
STX TY1
|
|
|
|
LDA FACES
|
|
AND #$20 ;Shares with 6
|
|
BNE :F3S4
|
|
|
|
JSR DRAW ;P5-P8
|
|
:F3S4 LDX P4X
|
|
STX TX2
|
|
LDX P4Y
|
|
STX TY2
|
|
|
|
LDA FACES
|
|
AND #$10 ;Shares with 5
|
|
BNE FACEDONE
|
|
|
|
JSR DRAW ;P8-P4
|
|
JMP FACEDONE
|
|
|
|
:FACE4 LDA K
|
|
SEC
|
|
SBC P2Z
|
|
BVS FACEDONE
|
|
CLC
|
|
ADC P1Z
|
|
BVC :DRAW4
|
|
LDA P1Z
|
|
:DRAW4 BPL FACEDONE
|
|
|
|
LDA P2X
|
|
STA TX1
|
|
LDA P2Y
|
|
STA TY1
|
|
|
|
LDA FACES
|
|
AND #$01 ;Shares with 1
|
|
BNE :F4S2
|
|
|
|
LDA P3X
|
|
STA TX2
|
|
LDA P3Y
|
|
STA TY2
|
|
JSR DRAW ;P2-P3
|
|
|
|
:F4S2 LDA P6X
|
|
STA TX2
|
|
LDA P6Y
|
|
STA TY2
|
|
|
|
LDA FACES
|
|
AND #$02 ;Shares with 2
|
|
BNE :F4S3
|
|
|
|
JSR DRAW ;P2-P6
|
|
:F4S3 LDA P7X
|
|
STA TX1
|
|
LDA P7Y
|
|
STA TY1
|
|
|
|
LDA FACES
|
|
AND #$20 ;Shares with 6
|
|
BNE :F4S4
|
|
|
|
JSR DRAW ;P6-P7
|
|
:F4S4 LDA P3X
|
|
STA TX2
|
|
LDA P3Y
|
|
STA TY2
|
|
|
|
LDA FACES
|
|
AND #$10 ;Shares with 5
|
|
BNE FACEDONE
|
|
|
|
JSR DRAW ;P7-P3
|
|
FACEDONE ;Whew! Time for a beer.
|
|
|
|
**** Now we need to unfill the outside from the faces
|
|
UNFILL LDY YMIN
|
|
:LOOP >>> SETBUF
|
|
LDX #08
|
|
:L1 LDA (BUFFER),Y
|
|
EOR #$FF ;Go till we find a plotted
|
|
BNE :GOTCHA ;point (i.e. A <> $FF)
|
|
* LDA #00 ;Unfilling as we go...
|
|
STA (BUFFER),Y
|
|
LDA #$80
|
|
STA BUFFER
|
|
LDA (BUFFER),Y
|
|
EOR #$FF
|
|
BNE :GOTCHA
|
|
* LDA #00
|
|
STA (BUFFER),Y
|
|
STA BUFFER
|
|
INC BUFFER+1
|
|
DEX ;This is our safety valve
|
|
BNE :L1 ;Really shouldn't need it
|
|
JSR CHOKE
|
|
JMP SWAPBUF
|
|
|
|
:GOTCHA ;A contains the EOR plot value
|
|
STA TEMP1 ;Now find the high bit
|
|
LDA #00
|
|
:L2 SEC
|
|
ROL
|
|
LSR TEMP1 ;Should really use a table
|
|
BNE :L2 ;for this!
|
|
AND (BUFFER),Y
|
|
STA (BUFFER),Y
|
|
|
|
LDA ZTEMP ;Now go to the end
|
|
;Carry is clear
|
|
;Actually we add 7
|
|
ADC #$06 ;16 columns of 128 bytes
|
|
STA BUFFER+1
|
|
LDA #$80
|
|
STA BUFFER
|
|
:LOOP2 LDA (BUFFER),Y ;And work backwards!
|
|
EOR #$FF
|
|
BNE :GOTCHA2
|
|
STA (BUFFER),Y
|
|
STA BUFFER ;Stick a zero into buffer
|
|
LDA (BUFFER),Y
|
|
EOR #$FF
|
|
BNE :GOTCHA2
|
|
STA (BUFFER),Y
|
|
LDA #$80
|
|
STA BUFFER
|
|
DEC BUFFER+1
|
|
BNE :LOOP2
|
|
|
|
:GOTCHA2 STA TEMP1 ;Again find the high bit
|
|
LDA #00
|
|
:L3 SEC
|
|
ROR
|
|
ASL TEMP1
|
|
BNE :L3
|
|
AND (BUFFER),Y
|
|
STA (BUFFER),Y
|
|
|
|
INY ;Now keep going
|
|
CPY YMAX
|
|
BCC :LOOP ;Until we hit ymax!
|
|
BEQ :LOOP ;We need the last one too.
|
|
|
|
**** Swap buffers
|
|
|
|
SWAPBUF LDA VMCSB
|
|
EOR #$02 ;Pretty tricky, eh?
|
|
STA VMCSB
|
|
LDA #$08
|
|
EOR ZTEMP ;ztemp=high byte just flips
|
|
STA ZTEMP ;between $30 and $38
|
|
|
|
JMP MAIN ;Around and around we go...
|
|
|
|
TXT 'Same thing we do every night, Pinky: '
|
|
TXT 'try to take over the world!'
|
|
|
|
|
|
*-------------------------------
|
|
* General questionable-value error procedure
|
|
|
|
CHOKE LDX #00
|
|
:LOOP LDA :CTEXT,X
|
|
BEQ :DONE
|
|
JSR CHROUT
|
|
INX
|
|
JMP :LOOP
|
|
:DONE RTS
|
|
:CTEXT HEX 0D ;CR
|
|
TXT 'something choked :('
|
|
HEX 0D00
|
|
|
|
TXT 'Narf!'
|
|
|
|
*-------------------------------
|
|
* Drawin' a line. A fahn lahn.
|
|
|
|
*** Some useful macros
|
|
|
|
PLOTPX MAC ;plot a point in x
|
|
PHA ;Use this one every time
|
|
LDA BITP,X ;X is increased
|
|
BMI C1
|
|
LDA #$80 ;Table has been rearranged
|
|
EOR BUFFER ;for filling faces
|
|
STA BUFFER
|
|
BMI C2
|
|
INC BUFFER+1
|
|
C2 LDA #%01111111 ;Note that this is changed
|
|
C1 AND (BUFFER),Y ;for plotting filled faces
|
|
STA (BUFFER),Y
|
|
PLA ;Need to save A!
|
|
<<<
|
|
|
|
PLOTPY MAC ;Plot a point in y: simpler and necessary!
|
|
PHA ;Use this one when you just increase Y
|
|
LDA BITP,X ;but X doesn't change
|
|
AND (BUFFER),Y
|
|
STA (BUFFER),Y
|
|
PLA
|
|
<<<
|
|
|
|
CINIT MAC ;Macro to initialize the counter
|
|
LDA ]1 ;dx or dy
|
|
LSR
|
|
EOR #$FF ;(Not really two's complement)
|
|
ADC #$01 ;A = 256-dx/2 or 256-dy/2
|
|
<<< ;The dx/2 makes a nicer looking line
|
|
|
|
XSTEP MAC ;Macro to take a step in X
|
|
XLOOP INX
|
|
ADC DY
|
|
BCC L1
|
|
* Do we use INY or DEY here?
|
|
IF I,]1 ;If the first character is an 'I'
|
|
INY
|
|
ELSE
|
|
DEY
|
|
FIN
|
|
SBC DX
|
|
L1 >>> PLOTPX ;Always take a step in X
|
|
CPX X2
|
|
BNE XLOOP
|
|
<<<
|
|
|
|
YSTEP MAC ;Same thing, but for Y
|
|
YLOOP IF I,]1
|
|
INY
|
|
ELSE
|
|
DEY
|
|
CLC ;Very important!
|
|
FIN
|
|
ADC DX
|
|
BCC L2
|
|
INX ;Always increase X
|
|
SBC DY
|
|
>>> PLOTPX
|
|
JMP L3
|
|
L2 >>> PLOTPY ;We only increased Y
|
|
L3 CPY Y2
|
|
BNE YLOOP
|
|
<<<
|
|
|
|
**** Initial line setup
|
|
|
|
DRAW >>> MOVE,TX1 ;X1 ;Move stuff into zero page
|
|
>>> MOVE,TX2 ;X2 ;Where it can be modified
|
|
>>> MOVE,TY1 ;Y1
|
|
>>> MOVE,TY2 ;Y2
|
|
>>> SETBUF ;Now we can clobber the buffer
|
|
|
|
SEC ;Make sure x1<x2
|
|
LDA X2
|
|
SBC X1
|
|
BCS :CONT
|
|
LDA Y2 ;If not, swap P1 and P2
|
|
LDY Y1
|
|
STA Y1
|
|
STY Y2
|
|
LDA X1
|
|
LDY X2
|
|
STY X1
|
|
STA X2
|
|
|
|
SEC
|
|
SBC X1 ;Now A=dx
|
|
:CONT STA DX
|
|
LDX X1 ;Put x1 into X, now we can trash X1
|
|
|
|
COLUMN LDA X1 ;Find the first column for X
|
|
LSR ;(This can be made much faster!)
|
|
LSR ;There are x1/8 128 byte blocks
|
|
LSR ;Which means x1/16 256 byte blocks
|
|
LSR
|
|
BCC :EVEN ;With a possible extra 128 byte block
|
|
LDY #$80 ;if so, set the high bit
|
|
STY BUFFER
|
|
CLC
|
|
:EVEN ADC BUFFER+1 ;Add in the number of 256 byte blocks
|
|
STA BUFFER+1 ;And store it!
|
|
|
|
SEC
|
|
LDA Y2 ;Calculate dy
|
|
SBC Y1
|
|
BCS :CONT2 ;Is y2>y1?
|
|
EOR #$FF ;Otherwise dy=y1-y2
|
|
ADC #$01
|
|
:CONT2 STA DY
|
|
CMP DX ;Who's bigger: dy or dx?
|
|
BCS STEPINY ;If dy, we need to take big steps in y
|
|
|
|
STEPINX LDY Y1 ;X is already set to x1
|
|
LDA BITP,X ;Plot the first point
|
|
AND (BUFFER),Y
|
|
STA (BUFFER),Y
|
|
>>> CINIT,DX ;Initialize the counter
|
|
CPY Y2
|
|
BCS XDECY ;Do we step forwards or backwards in Y?
|
|
|
|
XINCY >>> XSTEP,INY
|
|
RTS
|
|
|
|
STEPINY LDY Y1 ;Well, a little repetition never hurt anyone
|
|
LDA BITP,X
|
|
AND (BUFFER),Y
|
|
STA (BUFFER),Y
|
|
>>> CINIT,DY
|
|
CPY Y2
|
|
BCS YDECY
|
|
|
|
YINCY >>> YSTEP,INY
|
|
RTS
|
|
|
|
XDECY >>> XSTEP,DEY ;This is put here so that
|
|
RTS ;Branches are legal
|
|
|
|
YDECY >>> YSTEP,DEY
|
|
RTS
|
|
|
|
|
|
*-------------------------------
|
|
* Clean up
|
|
|
|
CLEANUP LDA VMCSB ;Switch char rom back in
|
|
AND #%11110101 ;default
|
|
STA VMCSB
|
|
|
|
RTS ;bye!
|
|
|
|
TXT 'Happy Holidays! '
|
|
TXT 'slj 12/94'
|
|
|
|
*-------------------------------
|
|
* Set up bit table
|
|
|
|
DS ^ ;Clear to end of page
|
|
;So that tables start on a page boundary
|
|
BITP LUP 16 ;128 Entries for X
|
|
DFB %01111111
|
|
DFB %10111111
|
|
DFB %11011111
|
|
DFB %11101111
|
|
DFB %11110111
|
|
DFB %11111011
|
|
DFB %11111101
|
|
DFB %11111110
|
|
--^
|
|
|
|
SIN ;Table of sines, 120 bytes
|
|
COS EQU SIN+128 ;Table of cosines
|
|
;Both of these trig tables are
|
|
;currently set up from BASIC
|
|
ZDIV EQU COS+128 ;Division table
|
|
TMATH EQU ZDIV+384 ;Math table of f(x)=x*x/256
|
|
|
|
And here are the native C64 files:
|
|
|
|
|
|
begin 600 cube3d2.0.lnx.uu
|
|
M`0A;"`H`ES4S,C@P+#`ZES4S,C@Q+#`ZES8T-BS"*#$V,BDZF2*3$1$1$1$1
|
|
M$1$B.IDB("`@("!54T4@3%E.6"!43R!$25-33TQ612!42$E3($9)3$4B.HDQ
|
|
M,`````T@,B`@*DQ93E@@6$E)($)9(%=)3$P@0T]23$59#2`U(`U#54)%,T0R
|
|
M+C`N3Z"@H*"@#2`Q,R`-4`T@,34U(`U#54)%,T0R+C`N4Z"@H*"@#2`X-B`-
|
|
M4`T@,C$X(`U#54)%,T0R+C$N3Z"@H*"@#2`Q,R`-4`T@,34U(`U)3DE4,T0R
|
|
M+C"@H*"@H*"@#2`Q,"`-4`T@-3`@#4Y/5$53,BXPH*"@H*"@H*`-(#0@#5`-
|
|
M(#$Y,R`-````````````````````````````````````````````````````
|
|
M````````````````````````````````````````````````````````````
|
|
M````````````````````````````````````````````````````````````
|
|
M````````````````````````````````````````````````````````````
|
|
M````````````````````````````````````````````````````````````
|
|
M``````````````````"`J0"-(-"-(="M&-`I#PD0C1C0H`"I'X7[J8"%_$Q9
|
|
M@9,%$1$1("`@("`@("`@("`@($-50D4S1"!6,BXP#0T@("`@("`@("`@("`@
|
|
M("`@("!"60V?("`@(%-415!(14X@2E5$1)D@("`@1T5/4D=%(%1!64Q/4@T-
|
|
MFR`@0TA%0TL@3U54(%1(12!*04XN(#DU($E34U5%($]b@($,]2$%#2TE.
|
|
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
|
|
MC#`,J8!%HX6C,`+FI*E_,:.1HVCD_=#=8*3\O0",,:.1HZ7Z2DG_:0'$_K!4
|
|
MR&7YD!WHY?I(O0",,`RI@$6CA:,P`N:DJ7\QHY&C:$P"BTB]`(PQHY&C:,3^
|
|
MT-%@Z&7ZD`.(Y?E(O0",,`RI@$6CA:,P`N:DJ7\QHY&C:.3]T-U@B!AE^9`=
|
|
MZ.7Z2+T`C#`,J8!%HX6C,`+FI*E_,:.1HVA,5XM(O0",,:.1HVC$_M#08*T8
|
|
MT"GUC1C08&A!4%!9(&A/3$E$05E3(2!33$H@,3(O.30`````````````````
|
|
M````````````````````````````````````````````````````````````
|
|
M````````````````````````````````````````````````````````````
|
|
M````````````````````````````````````?[_?[_?[_?Y_O]_O]_O]_G^_
|
|
MW^_W^_W^?[_?[_?[_?Y_O]_O]_O]_G^_W^_W^_W^?[_?[_?[_?Y_O]_O]_O]
|
|
M_G^_W^_W^_W^?[_?[_?[_?Y_O]_O]_O]_G^_W^_W^_W^?[_?[_?[_?Y_O]_O
|
|
M]_O]_G^_W^_W^_W^?[_?[_?[_?X!A?K%^;`XI/R]`(PQHY&CI?E*2?]I`<3^
|
|
ML&CH9?J0`\CE^4B]`(PP#*F`1:.%HS`"YJ2I?S&CD:-HY/W0W6"D_+T`C#&C
|
|
MD:.E^DI)_VD!Q/ZP5,AE^9`=Z.7Z2+T`C#`,J8!%`'`J*BHJ*BHJ*BHJ*BHJ
|
|
M*BHJ*BHJ*BHJ*BHJ*BHJ*BHJ*@TJH*"@H*"@H*"@H*"@H*"@H*"@H*"@H*"@
|
|
MH*"@H*"@*@TJH'-415!(14Z@:E5$1*"@H*"@H*"@H*"@H*"@H*"@*@TJH&=%
|
|
M3U)'1:!T05E,3U*@H*"@H*"@H*"@H*"@H*"@*@TJH'-405)4140ZH#<O,3$O
|
|
M.32@H*"@H*"@H*"@H*"@*@TJH&9)3DE32$5$.J`W+S$Y+SDTH*"@H*"@H*"@
|
|
MH*"@*@TJH%8R+C"@8T]-4$Q%5$5$.J`Q,B\Q-R\Y-*"@H*"@*@TJH*"@H*"@
|
|
MH*"@H*"@H*"@H*"@H*"@H*"@H*"@H*"@*@TJH'=%3$PLH$E&H$%,3*!'3T53
|
|
MH%=%3$R@5$A)4Z"@*@TJH%!23T=204V@5TE,3*!23U1!5$6@0:!#54)%+J"@
|
|
M*@TJH*"@H*"@H*"@H*"@H*"@H*"@H*"@H*"@H*"@H*"@*@TJH%8R+C"@*Z!N
|
|
M15>@04Y$H&E-4%)/5D5$(:"@H*"@*@TJH&Y/5Z!7251(H$9!4U1%4J!23U54
|
|
M24Y%4RR@H*"@*@TJH$A)1$1%3J!355)&04-%4RR@1DE,3$5$H*"@H*"@*@TJ
|
|
MH$9!0T53+*!!3D2@15A44D&@5$]0H%-%0U)%5*"@*@TJH%1%6%2@34534T%'
|
|
M15,AH*"@H*"@H*"@H*"@H*"@*@TJH*"@H*"@H*"@H*"@H*"@H*"@H*"@H*"@
|
|
MH*"@H*"@*@TJH'1(25.@4%)/1U)!3:!)4Z!)3E1%3D1%1*!43Z"@*@TJH$%#
|
|
M0T]-4$%.6:!42$6@05)424-,1:!)3J"@H*"@*@TJH&,]:$%#2TE.1RR@:D%.
|
|
M+J`Y-:!)4U-512Z@H*"@*@TJH&9/4J!$151!24Q3H$].H%1(25.@4%)/1U)!
|
|
M32R@*@TJH%)%042@5$A%H$%25$E#3$4AH*"@H*"@H*"@H*"@*@TJH*"@H*"@
|
|
MH*"@H*"@H*"@H*"@H*"@H*"@H*"@H*"@*@TJH'=2251%H%1/H%53(:"@H*"@
|
|
MH*"@H*"@H*"@H*"@*@TJH*"@H*"@H*"@H*"@H*"@H*"@H*"@H*"@H*"@H*"@
|
|
M*@TJH&U94T5,1J!72$5.H%E/54Y'H$1)1*"@H*"@H*"@*@TJH$5!1T523%F@
|
|
M1E)%455%3E2@H*"@H*"@H*"@H*"@*@TJH&1/0U1/4J!!3D2@<T%)3E0LH$%.
|
|
M1*!(14%21*"@*@TJH$=214%4H&%21U5-14Y4H*"@H*"@H*"@H*"@H*"@*@TJ
|
|
MH*!A0D]55*!)5*!!3D2@04)/550ZH$)55*"@H*"@*@TJH*!%5D5234]21:"@
|
|
MH*"@H*"@H*"@H*"@H*"@H*"@*@TJH&-!346@3U54H$)9H%1(1:!304U%H&1/
|
|
M3U*@H*"@*@TJH$%3H$E.H&F@5T5.5"Z@H*"@H*"@H*"@H*"@H*"@*@TJH*"@
|
|
MH"V@<E5"04E9052@H*"@H*"@H*"@H*"@H*"@*@TJH*"@H*"@H*"@H*"@H*"@
|
|
MH*"@H*"@H*"@H*"@H*"@*@TJH'1(3U5'2*!IH%-014%+H%=)5$B@5$A%H*"@
|
|
MH*"@*@TJH%1/3D=515.@3T:@345.H$%.1*!/1J!!3D=,15.@*@TJH$%.1*!(
|
|
M059%H$Y/5*!,3U9%+*!IH$%-H*"@H*"@*@TJH$)%0T]-1:!!4Z!33U5.1$E.
|
|
M1Z!"4D%34RR@3U*@*@TJH$&@5$E.2TQ)3D>@0UE-0D%,+J"@H*"@H*"@H*"@
|
|
M*@TJH*"@H"V@,:!C3U))3E1(24%.4Z`Q,Z"@H*"@H*"@*@TJH*"@H*"@H*"@
|
|
MH*"@H*"@H*"@H*"@H*"@H*"@H*"@*@TJH'`N<RZ@=$A)4Z!705.@5U))5%1%
|
|
M3J!54TE.1Z"@*@TJH*"@H*"@;4523$E.H#$R."Z@H*"@H*"@H*"@H*"@*@TJ
|
|
M*BHJ*BHJ*BHJ*BHJ*BHJ*BHJ*BHJ*BHJ*BHJ*BHJ*@T-(&]R9R`D.#`P,`T-
|
|
M*J!C3TY35$%.5%,-#6)U9F8Q(&5Q=2`D,S`P,"`[9DE24U2@0TA!4D%#5$52
|
|
MH%-%5`UB=69F,B!E<74@)#,X,#`@.W-%0T].1*!#2$%204-415*@4T54#6)U
|
|
M9F9E<B!E<74@)&$S(#MP4D5354U!0DQ9H%1(1:!405!%H%=/3B=4H$)%H%)5
|
|
M3DY)3D<->#$@97%U("1F8B`[<$])3E13H$9/4J!$4D%724Y'H$&@3$E.10UY
|
|
M,2!E<74@)&9C(#MT2$531:!:15)/H%!!1T6@041$4D534T53#7@R(&5Q=2`D
|
|
M9F0@.T1/3B=4H$-/3D9,24-4H%=)5$B@8F%S:6,->3(@97%U("1F90UD>"!E
|
|
M<74@)&8Y#61Y(&5Q=2`D9F$-=&5M<#$@97%U("1F8B`[;T:@0T]54E-%+*!#
|
|
M3U5,1*!#3TY&3$E#5*!7251(H%@Q#71E;7`R(&5Q=2`D9F,@.W1%35!/4D%2
|
|
M6:!605))04),15,->G1E;7`@97%U("0P,B`[=5-%1*!&3U*@0E5&1D52H%-7
|
|
M05`NH*!D3TXG5*!43U5#2"X->C$@97%U("0R,B`[=5-%1*!"6:!-051(H%)/
|
|
M551)3D4->C(@97%U("0R-"`[9$].)U2@5$]50TB@5$A%4T6@14E42$52(0UK
|
|
M(&5Q=2`D8C8@.V-/3E-404Y4H%53142@1D]2H$A)1$1%3@T@("`[4U521D%#
|
|
M1:!$151%0U1)3TZ@+:!$3TXG5*!43U5#2`UF86-E<R!E<74@)&(U(#MU4T5$
|
|
MH$E.H$A)1$1%3J!355)&04-%4RX->6UI;B!E<74@)&8W(#MU4T5$H$E.H$9)
|
|
M3$Q%1*!&04-%4Z`M+:!!4PUY;6%X(&5Q=2`D9C@@.U5354%,+*!$3TXG5*!4
|
|
M3U5#2`UA;F=M87@@97%U(#$R,"`[=$A%4D6@05)%H#(J4$DO04Y'34%8H$%.
|
|
M1TQ%4PT-*J!V:6,-#79M8W-B(&5Q=2`D9#`Q.`UB:V=N9"!E<74@)&0P,C`-
|
|
M8F]R9&5R(&5Q=2`D9#`R,0US<W1A<G0@97%U(#$S-#0@.U)/5Z`YH$E.H%-#
|
|
M4D5%3J!-14U/4EF@052@,3`R-`T-#2J@:T523D%,#0UC:')O=70@97%U("1F
|
|
M9F0R#6=E=&EN(&5Q=2`D9F9E-`T-*J!S3TU%H%9!4DE!0DQ%4PT-='@Q(#T@
|
|
M)#-F#71Y,2`]("0T,`UT>#(@/2`D-#$-='DR(#T@)#0R#7`Q>"`]("0Y,B`[
|
|
M=$A%4T6@05)%H%1%35!/4D%26:!35$]204=%#7`Q>2`]("0Y,R`[=5-%1*!)
|
|
M3J!03$]45$E.1Z!42$6@4%)/2D5#5$E/3@UP,G@@/2`D.30-<#)Y(#T@)#DU
|
|
M(#MT2$59H$%21:!(15)%H%-/H%1(052@5T4-<#-X(#T@)#DV(#M$3TXG5*!(
|
|
M059%H%1/H%)%0T%,0U5,051%H%1(14TN#7`S>2`]("1A90UP-'@@/2`D868@
|
|
M.W1(15F@34%+1:!,249%H$5!4UDN#7`T>2`]("1B,`UP-7@@/2`D8C$@.W=(
|
|
M6:!!4D6@64]5H$Q/3TM)3D>@052@346@3$E+1:!42$%4/PUP-7D@/2`D8C(@
|
|
M.V1/3B=4H%E/5:!44E535*!-13\-<#9X(#T@)&(S#7`V>2`]("1B-"`[:$%6
|
|
M24Y'H$%.3U1(15*@0TA)3$2@5T%33B=4H$U9H$E$14$N#7`W>"`]("0W,0UP
|
|
M-WD@/2`D-3`-<#AX(#T@)#4Q#7`X>2`]("0U,@UP,7H@/2`D-3<@.W1(15-%
|
|
MH$%21:!:+4-/3U)$24Y!5$53#7`R>B`]("0U."`[=T6@3TY,6:!.145$H%1(
|
|
M15-%H$9/55*@5$^@0TA%0TL-<#1Z(#T@)#4Y(#M&3U*@2$E$1$5.H$9!0T53
|
|
M#7`U>B`]("0V,`UD<W@@/2`D-C$@.V1S>*!)4Z!42$6@24Y#4D5-14Y4H$9/
|
|
M4@T@("`[4D]4051)3D>@05)/54Y$H%@-9'-Y(#T@)#8R(#MS24U)3$%2H$9/
|
|
M4J!D<WDLH&1S>@UD<WH@/2`D-C,-<W@@/2`D-C0@.W1(15-%H$%21:!42$6@
|
|
M04-454%,H$%.1TQ%4Z!)3J!8H%F@04Y$H%H-<WD@/2`D-C4-<WH@/2`D-C8-
|
|
M=#$@/2`D-C<@.W1(15-%H$%21:!54T5$H$E.H%1(1:!23U1!5$E/3@UT,B`]
|
|
M("0V.`UT,R`]("0V.2`[<T5%H%1(1:!!4E1)0TQ%H$9/4J!-3U)%H$1%5$%)
|
|
M3%,-=#0@/2`D-F$-=#4@/2`D-F(-=#8@/2`D-F,-=#<@/2`D-F0-=#@@/2`D
|
|
M-F4-=#D@/2`D-F8-=#$P(#T@)#<P#6$Q,2`]("1A-2`[=$A%4T6@05)%H%1(
|
|
M1:!%3$5-14Y44Z!/1J!42$6@4D]4051)3TZ@34%44DE8#6(Q,B`]("1A-B`[
|
|
M>'EZ#6,Q,R`]("1A-PUD,C$@/2`D83@@.W1(1:!.54U"15*@1$5.3U1%4Z`H
|
|
M4D]7+$-/3%5-3BD-93(R(#T@)&$Y#68R,R`]("1A80UG,S$@/2`D86(-:#,R
|
|
M(#T@)&%C#6DS,R`]("1A9`T-#2HJ*J!M04-23U,-#6UO=F4@;6%C#2!L9&$@
|
|
M73$-('-T82!=,@T@/#P\#0UG971K97D@;6%C("`[=T%)5*!&3U*@0:!+15E0
|
|
M4D534PUW86ET(&IS<B!G971I;@T@8VUP(",P,`T@8F5Q('=A:70-(#P\/`T-
|
|
M9&5B=6<@;6%C("`[<%))3E2@0:!#2$%204-415(-H"!D;Z`P("`[9$].)U2@
|
|
M05-314U"3$4-#2!L9&$@(UTQ#2!J<W(@8VAR;W5T#2`^/CX@9V5T:V5Y(#MA
|
|
M3D2@5T%)5*!43Z!#3TY424Y510T@8VUP(",G4R<@.VU9H%-%0U)%0U2@4U=)
|
|
M5$-(H$M%60T@8FYE(&PQ#2!J<W(@8VQE86YU<`T@:FUP(&1O;F4-;#$@8VUP
|
|
M(",G6"<@.VU9H%-%0U)%5*!!0D]25*!+15D-(&)N92!D;VYE#2!J;7`@8VQE
|
|
M86YU<`T@9FEN#61O;F4@/#P\#0UD96)U9V$@;6%C#2!D;Z`P#2!L9&$@73$-
|
|
M('-T82`Q,#(T#2!F:6X-9&]N96$@/#P\#0TJ+2TM+2TM+2TM+2TM+2TM+2TM
|
|
M+2TM+2TM+2TM+2TM+0T-(&QD82`C)#`P#2!S=&$@8FMG;F0-('-T82!B;W)D
|
|
M97(-(&QD82!V;6-S8@T@86YD(",E,#`P,#$Q,3$@.W-#4D5%3J!-14U/4EF@
|
|
M5$^@,3`R-`T@;W)A(",E,#`P,3`P,#`-('-T82!V;6-S8@T-(&QD>2`C,#`-
|
|
M(&QD82`C/'1T97AT#2!S=&$@=&5M<#$-(&QD82`C/G1T97AT#2!S=&$@=&5M
|
|
M<#(-(&IM<"!T:71L90UT=&5X="!H97@@.3,P-3$Q,3$Q,2`[0TQ%05*@4T-2
|
|
M145.+*!72$E412R@0U)34J!$3@T@='AT(">@H*"@H*"@H*"@H*"@0U5"13-$
|
|
MH%8R+C`G+#!$+#!$#2!T>'0@)Z"@H*"@H*"@H*"@H*"@H*"@H$)9)RPP1`T@
|
|
M:&5X(#EF(#M#64%.#2!T>'0@)Z"@H*!35$502$5.H$I51$0G#2!H97@@.3D-
|
|
M('1X="`GH*"@H$=%3U)'1:!405E,3U(G+#!$+#!$#2!H97@@.6(-('1X="`G
|
|
MH*!#2$5#2Z!/552@5$A%H$I!3BZ@.36@25-3546@3T8G+#!$#2!H97@@.38-
|
|
M('1X="`GH*!#/4A!0TM)3D<G#2!H97@@.6(-('1X="`GH$9/4J!-3U)%H$1%
|
|
M5$%)3%,A)RPP1`T@:&5X(#!D,60Q9#EE,3(-('1X="`G1C$O1C(G+#DR#2!T
|
|
M>'0@)Z`MH$E.0R]$14.@6"U23U1!5$E/3B<L,$0-(&AE>"`Q9#%D,3(-('1X
|
|
M="`G1C,O1C0G+#DR#2!T>'0@)Z`MH$E.0R]$14.@62U23U1!5$E/3B<L,$0-
|
|
M(&AE>"`Q9#%D,3(-('1X="`G1C4O1C8G+#DR#2!T>'0@)Z`MH$E.0R]$14.@
|
|
M6BU23U1!5$E/3B<L,$0-(&AE>"`Q9#%D,3(-('1X="`G1C<G+#DR#2!T>'0@
|
|
M)Z!215-%5%,G+#!$#2!T>'0@)Z"@4%)%4U.@4:!43Z!154E4)RPP1`T@:&5X
|
|
M(#!D,#4-('1X="`GH*"@H*"@4%)%4U.@04Y9H$M%6:!43Z!"14=)3B<L,$0-
|
|
M(&AE>"`P,`UT:71L92!L9&$@*'1E;7`Q*2QY#2!B97$@.F-O;G0-(&IS<B!C
|
|
M:')O=70-(&EN>0T@8FYE('1I=&QE#2!I;F,@=&5M<#(-(&IM<"!T:71L90T@
|
|
M='AT("=T2$E3H$E3H$&@4T5#4D54H%1%6%2@34534T%'12$G#3IC;VYT(#X^
|
|
M/B!G971K97D-#2HJ*BJ@<T54H%50H%1!0DQ%4R@_*0T-*J!T04),15.@05)%
|
|
MH$-54E)%3E1,6:!3152@55"@24Z@8F%S:6,-*J!!3D2@0EF@5$A%H$%34T5-
|
|
M0DQ%4BX-#71A8FQE<R!L9&$@(SYT;6%T:`T@<W1A('HQ*S$-('-T82!Z,BLQ
|
|
M#0TJ*BHJH&-,14%2H%-#4D5%3J!!3D2@4T54H%50H")"251-05`B#7-E='5P
|
|
M(&QD82`C)#`Q(#MW2$E410T@<W1A("1D,#(Q(#MT2$E3H$E3H$1/3D6@4T^@
|
|
M5$A!5*!/3$1%4@T@;&1A(",Q-#<@.TU!0TA)3D53H%=)3$R@4T54H%50#2!J
|
|
M<W(@8VAR;W5T#2!L9&$@(R0P,"`[0T]24D5#5$Q9#2!S=&$@)&0P,C$-(&QD
|
|
M82`C/'-S=&%R=`T@861C(",Q,B`[=$A%H$=/04R@25.@5$^@0T5.5$52H%1(
|
|
M1:!'4D%02$E#4PT@<W1A('1E;7`Q(#MC3TQ534Z@,3(-(&QD82`C/G-S=&%R
|
|
M="`[<D]7H#D-('-T82!T96UP,2LQ(#MS<W1A<G2@4$])3E13H%1/H%)/5Z`Y
|
|
M#2!L9&$@(S`P#2!L9'D@(S`P#2!L9'@@(S`P(#M8H%=)3$R@0T]53E2@,3:@
|
|
M4D]74Z!&3U*@55,-(&-L8PT-.FQO;W`@<W1A("AT96UP,2DL>0T@:6YY#2!A
|
|
M9&,@(S$V#2!B8V,@.FQO;W`-(&-L8PT@;&1A('1E;7`Q#2!A9&,@(S0P(#MN
|
|
M145$H%1/H$%$1*`T,*!43Z!42$6@0D%31:!03TE.5$52#2!S=&$@=&5M<#$@
|
|
M.W1/H$I535"@5$^@5$A%H$Y%6%2@4D]7#2!L9&$@=&5M<#$K,0T@861C(",P
|
|
M,"`[=$%+1:!#05)%H$]&H$-!4E))15,-('-T82!T96UP,2LQ#2!L9'D@(S`P
|
|
M#2!I;G@-('1X82`@.WB@25.@04Q33Z!!3J!)3D1%6*!)3E1/H%1(1:!#2$%2
|
|
M04-415*@3E5-0D52#2!C<'@@(S$V#2!B;F4@.FQO;W`@.VY%142@5$^@1$^@
|
|
M252@,3:@5$E-15,-#2`^/CX@9&5B=6<L)S(G#2HJ*BJ@<T54H%50H$)51D9%
|
|
M4E,-#2!L9&$@(SQB=69F,0T@<W1A(&)U9F9E<@T@;&1A(",^8G5F9C$-('-T
|
|
M82!B=69F97(K,0T@<W1A('IT96UP(#M:5$5-4*!724Q,H$U!2T6@3$E&1:!3
|
|
M24U03$6@1D]2H%53#2!L9&$@=FUC<V(-(&%N9"`C)3$Q,3$P,#`Q(#MS5$%2
|
|
M5*!(15)%H%-/H%1(052@4U=!4*!"549&15)3H%=)3$R@5T]22Z!224=(5`T@
|
|
M;W)A(",E,#`P,#$Q,3`-('-T82!V;6-S8@T-*BHJ*J!S152@55"@24Y)5$E!
|
|
M3*!604Q515,-#6EN:70@;&1A(",P,`T@<W1A(&1S>`T@<W1A(&1S>0T@<W1A
|
|
M(&1S>@T@<W1A('-X#2!S=&$@<WD-('-T82!S>@T-(#X^/B!D96)U9RPG-"<-
|
|
M#2HM+2TM+2TM+2TM+2TM+2TM+2TM+2TM+2TM+2TM+2TM#2J@;4%)3J!,3T]0
|
|
M#0TJ*BHJH&=%5*!+15E04D534PT-;6%I;@T@8VQI#6MP<F5S<R!J<W(@9V5T
|
|
M:6X-(&-M<"`C,3,S(#MF,3\-(&)N92`Z9C(-(&QD82!D<W@-(&-M<"`C86YG
|
|
M;6%X+S(@.VY/H$U/4D6@5$A!3J!020T@8F5Q(#IC;VYT#2!I;F,@9'-X(#M/
|
|
M5$A%4E=)4T6@24Y#4D5!4T6@6"U23U1!5$E/3@T@:FUP(#IC;VYT#3IF,B!C
|
|
M;7`@(S$S-R`[9C(_#2!B;F4@.F8S#2!L9&$@9'-X#2!B97$@.F-O;G0-(&1E
|
|
M8R!D<W@-(&IM<"`Z8V]N=`TZ9C,@8VUP(",Q,S0-(&)N92`Z9C0-(&QD82!D
|
|
M<WD-(&-M<"`C86YG;6%X+S(-(&)E<2`Z8V]N=`T@:6YC(&1S>2`[:4Y#4D5!
|
|
M4T6@62U23U1!5$E/3@T@:FUP(#IC;VYT#3IF-"!C;7`@(S$S.`T@8FYE(#IF
|
|
M-0T@;&1A(&1S>0T@8F5Q(#IC;VYT#2!D96,@9'-Y#2!J;7`@.F-O;G0-.F8U
|
|
M(&-M<"`C,3,U#2!B;F4@.F8V#2!L9&$@9'-Z#2!C;7`@(V%N9VUA>"\R#2!B
|
|
M97$@.F-O;G0-(&EN8R!D<WH@.UHM4D]4051)3TX-(&IM<"`Z8V]N=`TZ9C8@
|
|
M8VUP(",Q,SD-(&)N92`Z9C<-(&QD82!D<WH-(&)E<2`Z8V]N=`T@9&5C(&1S
|
|
M>@T@:FUP(#IC;VYT#3IF-R!C;7`@(S$S-@T@8FYE(#IQ#2!J;7`@:6YI=`TZ
|
|
M<2!C;7`@(R=1)R`[4:!154E44PT@8FYE(#IC;VYT#2!J;7`@8VQE86YU<`T-
|
|
M.F-O;G0@<V5I("`[<U!%142@5$A)3D=3H%50H$&@0DE4#2J@/CX^H&1E8G5G
|
|
M+"<U)PT-*BHJ*J!U4$1!5$6@04Y'3$53#0UU<&1A=&4@8VQC#2!L9&$@<W@-
|
|
M(&%D8R!D<W@-(&-M<"`C86YG;6%X(#MA4D6@5T6@/CV@34%824U53:!!3D=,
|
|
M13\-(&)C8R`Z8V]N=#$-('-B8R`C86YG;6%X(#II1B!33RP@4D53150-.F-O
|
|
M;G0Q('-T82!S>`T@8VQC#2!L9&$@<WD-(&%D8R!D<WD-(&-M<"`C86YG;6%X
|
|
M#2!B8V,@.F-O;G0R#2!S8F,@(V%N9VUA>"`[<T%-1:!$14%,#3IC;VYT,B!S
|
|
M=&$@<WD-(&-L8PT@;&1A('-Z#2!A9&,@9'-Z#2!C;7`@(V%N9VUA>`T@8F-C
|
|
M(#IC;VYT,PT@<V)C("-A;F=M87@-.F-O;G0S('-T82!S>@T-*BHJ*J!R3U1!
|
|
M5$6@0T]/4D1)3D%415,-#7)O=&%T90T-*BHJH&9)4E-4+*!#04Q#54Q!5$6@
|
|
M5#$L5#(L+BXN+%0Q,`T-*BJ@=%=/H$U!0U)/4Z!43Z!324U03$E&6:!/55*@
|
|
M3$E&10UA9&1A(&UA8R`@.V%$1*!45T^@04Y'3$53H%1/1T542$52#2!C;&,-
|
|
M(&QD82!=,0T@861C(%TR#2!C;7`@(V%N9VUA>"`[:5.@5$A%H%-53:`^H#(J
|
|
M4$D_#2!B8V,@9&]N90T@<V)C("-A;F=M87@@.VE&H%-/+*!354)44D%#5*`R
|
|
M*E!)#61O;F4@/#P\#0US=6)A(&UA8R`@.W-50E1204-4H%173Z!!3D=,15,-
|
|
M('-E8PT@;&1A(%TQ#2!S8F,@73(-(&)C<R!D;VYE#2!A9&,@(V%N9VUA>"`[
|
|
M;T]04RR@5T6@3D5%1*!43Z!!1$2@,BI020UD;VYE(#P\/`T-*BJ@;D]7H$-!
|
|
M3$-53$%41:!4,2Q4,BQ%5$,N#0T@/CX^('-U8F$L<WD[<WH-('-T82!T,2`[
|
|
M5#$]4UDM4UH-(#X^/B!A9&1A+'-Y.W-Z#2!S=&$@=#(@.U0R/5-9*U-:#2`^
|
|
M/CX@861D82QS>#MS>@T@<W1A('0S(#M4,SU36"M36@T@/CX^('-U8F$L<W@[
|
|
M<WH-('-T82!T-"`[5#0]4U@M4UH-(#X^/B!A9&1A+'-X.W0R#2!S=&$@=#4@
|
|
M.U0U/5-8*U0R#2`^/CX@<W5B82QS>#MT,0T@<W1A('0V(#M4-CU36"U4,0T@
|
|
M/CX^(&%D9&$L<W@[=#$-('-T82!T-R`[5#<]4U@K5#$-(#X^/B!S=6)A+'0R
|
|
M.W-X#2!S=&$@=#@@.U0X/50R+5-8#2`^/CX@<W5B82QS>3MS>`T@<W1A('0Y
|
|
M(#M4.3U362U36`T@/CX^(&%D9&$L<W@[<WD-('-T82!T,3`@.U0Q,#U36"M3
|
|
M60T-*J!E5*!63TE,02$-#2HJ*J!N15A4+*!#04Q#54Q!5$6@82QB+&,L+BXN
|
|
M+&D-#2HJH&%.3U1(15*@55-%1E5,H$Q)5%1,1:!-04-23PUD:78R(&UA8R`@
|
|
M.V1)5DE$1:!!H%-)1TY%1*!.54U"15*@0EF@,@T[:52@25.@05-354U%1*!4
|
|
M2$%4H%1(1:!.54U"15(-(&)P;"!P;W,@.TE3H$E.H%1(1:!!0T-5355,051/
|
|
M4@T@8VQC#2!E;W(@(R1F9B`[=T6@3D5%1*!43Z!53BU.14=!5$E61:!42$6@
|
|
M3E5-0D52#2!A9&,@(S`Q(#M"6:!404M)3D>@250G4Z!#3TU03$5-14Y4#2!L
|
|
M<W(@(#M$259)1$6@0EF@5%=/#2!C;&,-(&5O<B`C)&9F#2!A9&,@(S`Q(#MM
|
|
M04M%H$E4H$Y%1T%4259%H$%'04E.#2!J;7`@9&]N961I=@UP;W,@;'-R("`[
|
|
M;E5-0D52H$E3H%!/4TE4259%#61O;F5D:78@/#P\#0UM=6PR(&UA8R`@.VU5
|
|
M3%1)4$Q9H$&@4TE'3D5$H$Y534)%4J!"6:`R#2!B<&P@<&]S;0T@8VQC#2!E
|
|
M;W(@(R1F9@T@861C(",D,#$-(&%S;`T@8VQC#2!E;W(@(R1F9@T@861C(",D
|
|
M,#$-(&IM<"!D;VYE;75L#7!O<VT@87-L#61O;F5M=6P@/#P\#0TJ*J!N3U1%
|
|
MH%1(052@5T6@05)%H$-54E)%3E1,6:!-04M)3D>@0:!-24Y/4J!,14%0#2HJ
|
|
MH$]&H$9!251(H%1(052@3D^@3U9%4D9,3U=3H%=)3$R@3T-#55(N#0TZ8V%L
|
|
M8V$@8VQC#2!L9'@@=#$-(&QD82!C;W,L>`T@;&1X('0R#2!A9&,@8V]S+'@-
|
|
M('-T82!A,3$@.V$]*$-/4RA4,2DK0T]3*%0R*2DO,@TZ8V%L8V(@;&1X('0Q
|
|
M#2!L9&$@<VEN+'@-('-E8PT@;&1X('0R#2!S8F,@<VEN+'@-('-T82!B,3(@
|
|
M.V(]*%-)3BA4,2DM4TE.*%0R*2DO,@TZ8V%L8V,@;&1X('-Y#2!L9&$@<VEN
|
|
M+'@-(#X^/B!M=6PR#2!S=&$@8S$S(#MC/5-)3BA362D-.F-A;&-D('-E8PT@
|
|
M;&1X('0X#2!L9&$@8V]S+'@-(&QD>"!T-PT@<V)C(&-O<RQX#2!S96,-(&QD
|
|
M>"!T-0T@<V)C(&-O<RQX#2!C;&,-(&QD>"!T-@T@861C(&-O<RQX(#MD23TH
|
|
M0T]3*%0X*2U#3U,H5#<I*T-/4RA4-BDM0T]3*%0U*2DO,@T@/CX^(&1I=C(-
|
|
M(&-L8PT@;&1X('0S#2!A9&,@<VEN+'@-('-E8PT@;&1X('0T#2!S8F,@<VEN
|
|
M+'@-('-T82!D,C$@.V0]*%-)3BA4,RDM4TE.*%0T*2MD22DO,@TZ8V%L8V4@
|
|
M<V5C#2!L9'@@=#4-(&QD82!S:6XL>`T@;&1X('0V#2!S8F,@<VEN+'@-('-E
|
|
M8PT@;&1X('0W#2!S8F,@<VEN+'@-('-E8PT@;&1X('0X#2!S8F,@<VEN+'@@
|
|
M.V5)/2A324XH5#4I+5-)3BA4-BDM4TE.*%0W*2U324XH5#@I*2\R#2`^/CX@
|
|
M9&EV,@T@8VQC#2!L9'@@=#,-(&%D8R!C;W,L>`T@8VQC#2!L9'@@=#0-(&%D
|
|
M8R!C;W,L>`T@<W1A(&4R,B`[93TH0T]3*%0S*2M#3U,H5#0I*V5)*2\R#3IC
|
|
M86QC9B!L9'@@=#D-(&QD82!S:6XL>`T@<V5C#2!L9'@@=#$P#2!S8F,@<VEN
|
|
M+'@-('-T82!F,C,@.V8]*%-)3BA4.2DM4TE.*%0Q,"DI+S(-.F-A;&-G(&QD
|
|
M>"!T-@T@;&1A('-I;BQX#2!S96,-(&QD>"!T.`T@<V)C('-I;BQX#2!S96,-
|
|
M(&QD>"!T-PT@<V)C('-I;BQX#2!S96,-(&QD>"!T-0T@<V)C('-I;BQX(#MG
|
|
M23TH4TE.*%0V*2U324XH5#@I+5-)3BA4-RDM4TE.*%0U*2DO,@T@/CX^(&1I
|
|
M=C(-(&-L8PT@;&1X('0T#2!A9&,@8V]S+'@-('-E8PT@;&1X('0S#2!S8F,@
|
|
M8V]S+'@-('-T82!G,S$@.V<]*$-/4RA4-"DM0T]3*%0S*2MG22DO,@T@/CX^
|
|
M(&1E8G5G82QG,S$-(#X^/B!D96)U9RPG1R<-.F-A;&-H(&-L8PT@;&1X('0V
|
|
M#2!L9&$@8V]S+'@-(&QD>"!T-PT@861C(&-O<RQX#2!S96,-(&QD>"!T-0T@
|
|
M<V)C(&-O<RQX#2!S96,-(&QD>"!T.`T@<V)C(&-O<RQX(#MH23TH0T]3*%0V
|
|
M*2M#3U,H5#<I+4-/4RA4-2DM0T]3*%0X*2DO,@T@/CX^(&1I=C(-(&-L8PT@
|
|
M;&1X('0S#2!A9&,@<VEN+'@-(&-L8PT@;&1X('0T#2!A9&,@<VEN+'@-('-T
|
|
M82!H,S(@.V@]*%-)3BA4,RDK4TE.*%0T*2MH22DO,@TZ=VAE=R!C;&,-(&QD
|
|
M>"!T.0T@;&1A(&-O<RQX#2!L9'@@=#$P#2!A9&,@8V]S+'@-('-T82!I,S,@
|
|
M.VD]*$-/4RA4.2DK0T]3*%0Q,"DI+S(-#2HJH&E4)U.@04Q,H$1/5TY(24Q,
|
|
MH$923TV@2$5212X-(&IM<"!D;W=N:&EL;`T@='AT("=G146@8E)!24XLH%=(
|
|
M052@1$^@64]5H%=!3E2@5$^@1$^@)PT@='AT("=43TY)1TA4/R<-#2HJH')/
|
|
M5$%412R@4%)/2D5#5"R@04Y$H%-43U)%H%1(1:!03TE.5%,-9&]W;FAI;&P-
|
|
M#2J@8:!.14%4H$U!0U)/#6YE9R!M86,@(#MC2$%.1T6@5$A%H%-)1TZ@3T:@
|
|
M0:!45T\G4Z!#3TU03$5-14Y4#2!C;&,-(&QD82!=,2`[3E5-0D52+@T@96]R
|
|
M(",D9F8-(&%D8R`C)#`Q#2`\/#P-#2HM+2TM+2TM+2TM+2TM+2TM+2TM+2TM
|
|
M+2TM+2TM+2TM#2J@=$A%4T6@34%#4D]3H%)%4$Q!0T6@5$A%H%!2159)3U53
|
|
MH%!23TI%0U1)3TX-*J!354)23U5424Y%+@T-<VUU;'0@;6%C(#MM54Q425!,
|
|
M6:!45T^@4TE'3D5$H#@M0DE4#2`@(#M.54U"15)3.J!A*GDO-C2@+3Z@80T@
|
|
M<W1A('HQ#2!C;&,-(&5O<B`C)&9F#2!A9&,@(R0P,0T@<W1A('HR#2!L9&$@
|
|
M*'HQ*2QY#2!S96,-('-B8R`H>C(I+'D-(#P\/"`@.V%,3*!$3TY%H#HI#0T-
|
|
M861D<W5B(&UA8R`@.V%$1*!/4J!354)44D%#5*!45T^@3E5-0D524PT@("`[
|
|
M1$5014Y$24Y'H$].H$9)4E-4H$E.4%54#2!I9B`M/5TQ(#MI1J!354)44D%#
|
|
M5`T@<V5C("`[5$A%3J!54T6@5$A)4Z!#3T1%#2!S8F,@73(-(&5L<V4@(#M/
|
|
M5$A%4E=)4T6@55-%H%1(25.@0T]$10T@8VQC#2!A9&,@73(-(&9I;@T@/#P\
|
|
M#0T-<')O:F5C="!M86,@(#MT2$6@04-454%,H%!23TI%0U1)3TZ@4D]55$E.
|
|
M10T@("`[5%=/H$E.4%544Z!!4D6@55-%1*`H6"Q9*0T@("`[0T]24D534$].
|
|
M1$E.1Z!43Z`H*R\M,2PK+RTQ*0T@("`[=$A%H%1(25)$H$E.4%54H$E3H%53
|
|
M142@5$\-("`@.T1%5$5234E.1:!)1J!42$6@4D]4051%1`T@("`[6BU#3T]2
|
|
M1$E.051%H%-(3U5,1*!"10T@("`[4U1/4D5$+*!!3D2@24:@4T^@5TA%4D4N
|
|
M#2`@(#MT2$6@0T%,3$E.1Z!23U5424Y%H$A!3D1,15,-("`@.T-(04Y'24Y'
|
|
MH%1(1:!324=.H$]&H%HN#0T@;&1A(&DS,R`[8T%,0U5,051%H%)/5$%4142@
|
|
M6CH-(#X^/B!A9&1S=6(L73$[9S,Q(#MA1$2@3U*@4U5"5%)!0U2@6`T@/CX^
|
|
M(&%D9'-U8BQ=,CMH,S(@.V%$1*!/4J!354)44D%#5*!9#2!I9B!P+%TS(#MD
|
|
M3Z!71:!.145$H%1/H%-43U)%H%1(1:!03TE.5#\-('-T82!=,R`[=$A%3J!$
|
|
M3Z!33R$-(&9I;@TJH&5O<J`C,3(XH#MW1:!!4D6@1T])3D>@5$^@5$%+1:`Q
|
|
M,C@K6@T@=&%X("`[;D]7H$E4H$E3H%)%0419H$9/4J!)3D1%6$E.1PT@;&1A
|
|
M('ID:78L>"`[=$%"3$6@3T:@1"\H6BM:,"D-('1A>2`@.WF@3D]7H$-/3E1!
|
|
M24Y3H%!23TI%0U1)3TX-#2!L9&$@8S$S(#MN3U>@0T%,0U5,051%H%)/5$%4
|
|
M142@6`T@/CX^(&%D9'-U8BQ=,3MA,3$-(#X^/B!A9&1S=6(L73([8C$R#2`^
|
|
M/CX@<VUU;'0@.W-)1TY%1*!-54Q425!,6:!A*GDO-C0M/F$-(&-L8PT@861C
|
|
M(",V-"`[;T9&4T54H%1(1:!#3T]21$E.051%#2!T87@@(#MN3U>@>*!)4Z!2
|
|
M3U1!5$5$H%@A#0T@;&1A(&8R,R`[;D]7H$E4)U.@62=3H%154DX-(#X^/B!A
|
|
M9&1S=6(L73$[9#(Q#2`^/CX@861D<W5B+%TR.V4R,@T@/CX^('-M=6QT#2!C
|
|
M;&,-(&%D8R`C-C0@.V]&1E-%5`T@8VUP('EM:6X@.V9)1U521:!/552@24:@
|
|
M252@25.@00T@8F-S(&YO=&UI;B`[34E.H$]2H$U!6*!604Q51:!&3U*@60T@
|
|
M<W1A('EM:6X-(&)C8R!N;W1M87@@.W1(25.@25.@55-%1*!)3J!#04Q#54Q!
|
|
M5$E.1PUN;W1M:6X@8VUP('EM87@@.U1(1:!&24Q,142@1D%#15,-(&)C8R!N
|
|
M;W1M87@-('-T82!Y;6%X#6YO=&UA>"!T87D@(#MN3U2@4D5!3$Q9H$Y%0T53
|
|
M4T%260T-(#P\/"`@.V%,3*!$3TY%#0T-(&QD82`C-C0@.W)%4T54H'E-24Z@
|
|
M04Y$H'E-05@-('-T82!Y;6EN#2!S=&$@>6UA>`T-*J!P,3U;,:`QH#%=#2`^
|
|
M/CX@<')O:F5C="PQ.S$[<#%Z(#MR3U1!5$5$H%J@4U1/4D5$H$E.H'`Q>@T@
|
|
M<W1X('`Q>`T@<W1Y('`Q>0TJH'`R/5LQH"TQH#%=#2`^/CX@<')O:F5C="PQ
|
|
M.RTQ.W`R>@T@<W1X('`R>`T@<W1Y('`R>0TJH'`S/5LM,:`M,:`Q70T@/CX^
|
|
M('!R;VIE8W0L+3$[+3$[;F]P92`[9$].)U2@4U1/4D6@6BU604Q510T@<W1X
|
|
M('`S>`T@<W1Y('`S>0TJH'`T/5LM,:`QH#%=#2`^/CX@<')O:F5C="PM,3LQ
|
|
M.W`T>@T@<W1X('`T>`T@<W1Y('`T>0TJH'`X/5LM,:`QH"TQ70T@/CX^(&YE
|
|
M9RQC,3,-('-T82!C,3,-(#X^/B!N96<L9C(S#2!S=&$@9C(S#2`^/CX@;F5G
|
|
M+&DS,PT@<W1A(&DS,PT@/CX^('!R;VIE8W0L+3$[,3MN;W!E#2!S='@@<#AX
|
|
M#2!S='D@<#AY#2J@<#<]6RTQH"TQH"TQ70T@/CX^('!R;VIE8W0L+3$[+3$[
|
|
M;F]P90T@<W1X('`W>`T@<W1Y('`W>0TJH'`V/5LQH"TQH"TQ70T@/CX^('!R
|
|
M;VIE8W0L,3LM,3MN;W!E#2!S='@@<#9X#2!S='D@<#9Y#2J@<#4]6S&@,:`M
|
|
M,5T-(#X^/B!P<F]J96-T+#$[,3MP-7H-('-T>"!P-7@-('-T>2!P-7D-#2J@
|
|
M8:!,25143$6@34%#4D\-#7-E=&)U9B!M86,@(#MP552@0E5&1D524Z!72$52
|
|
M1:!42$59H$-!3J!"1:!(55)4#2!L9&$@(S`P#2!S=&$@8G5F9F5R#2!L9&$@
|
|
M>G1E;7`@.UI414U0H$-/3E1!24Y3H%1(1:!(24=(H$)95$6@2$5210T@<W1A
|
|
M(&)U9F9E<BLQ#2`\/#P-#2HJ*BJ@8TQ%05*@0E5&1D52#0TJH#X^/J!S971B
|
|
M=68-*F-L<F)U9J!L9&&@(R0P,*`[<%)%5%19H%-44D%)1TA41D]25T%21"P-
|
|
M*J!L9'B@(R0P.*`[::!42$E.2PTJH&QD>:`C)#`P#2HZ;&]O<*!S=&&@*&)U
|
|
M9F9E<BDL>0TJH&EN>0TJH&)N9:`Z;&]O<`TJH&EN8Z!B=69F97(K,0TJH&1E
|
|
M>`TJH&)N9:`Z;&]O<`T-*J!T2$E3H$E3H%1(1:!.15>@04Y$H$E-4%)/5D5$
|
|
MH$)51D9%4J!#3$5!4@TJH%)/551)3D6@1D]2H$9)3$Q%1*!&04-%4PT-(#X^
|
|
M/B!S971B=68-('-T82!T96UP,2LQ(#M"549&15(RH%=)3$R@4$])3E2@5$\-
|
|
M(&QD82`C)#@P(#M"549&15(K,3(X#2!S=&$@=&5M<#$@.VU!2T53H$Q)1D6@
|
|
M1D%35$52H$9/4J!54PUF:6QC;'(@;&1A(",P,`T@;&1X(",D,#@@.W=%)TQ,
|
|
MH$1/H$E4H%173Z!!5*!!H%1)344-(&QD>2`C)#`P#3IL;V]P,2!S=&$@*&)U
|
|
M9F9E<BDL>0T@<W1A("AT96UP,2DL>0T@:6YY#2!C<'D@>6UI;@T@8FYE(#IL
|
|
M;V]P,0T@;&1A(",D9F8@.VY/5Z!,3T%$H%=)5$B@1DE,3%,-.FQO;W`R('-T
|
|
M82`H8G5F9F5R*2QY#2!S=&$@*'1E;7`Q*2QY#2!I;GD-(&-P>2!Y;6%X#2!B
|
|
M8V,@.FQO;W`R#2!L9&$@(R0P,"`[8DQ!0TN@3U54H%1(1:!215-4#3IL;V]P
|
|
M,R!S=&$@*&)U9F9E<BDL>0T@<W1A("AT96UP,2DL>0T@:6YY#2!B<&P@.FQO
|
|
M;W`S(#MU3E1)3*!Y/3$R.`T@;&1Y(",P,`T@:6YC(&)U9F9E<BLQ#2!I;F,@
|
|
M=&5M<#$K,0T@9&5X#2!B;F4@.FQO;W`Q(#MG3Z!!3$R@5$A%H%=!6:!!4D]5
|
|
M3D0-#2HJ*BJ@;D]7H$1205>@5$A%H$Q)3D53+@TJ*BHJH&)55*!&25)35*!#
|
|
M2$5#2Z!&3U*@2$E$1$5.H$9!0T53(0TJ*BHJH')%345-0D52.J!P,3U;,:`Q
|
|
MH#%=H'`R/5LQH"TQH#%=H'`S/5LM,:`M,:`Q70TJ*BHJH'`T/5LM,:`QH#%=
|
|
MH'`U/5LQH#&@+3%=H'`V/5LQH"TQH"TQ7:!P-SU;+3&@+3&@+3%=#2HJ*BJ@
|
|
M<#@]6RTQH#&@+3%=#0UL:6YE<R!L9&$@(S`P#2!S=&$@9F%C97,@.VA)1$1%
|
|
M3J!&04-%H$-/54Y415(-.F9A8V4Q(&QD82!K#2!S96,-('-B8R!P,7H-(&)V
|
|
M<R`Z9F%C938@.V]615)&3$]7H$%,4D5!1%D_#2!C;&,-(&%D8R!P-7H@.VE3
|
|
MH$LM5C%:H#R@,#\-(#MI1J!.3U0LH$9!0T6@25.@24Y625-)0DQ%#2!B=F,@
|
|
M.F1R87<Q(#MB552@5T6@34E'2%2@2$%61:!/5D521DQ/5PT@;&1A('`U>B`[
|
|
M=T%3H$]615)&3$]7H%!/4Z!/4J!.14<_#3ID<F%W,2!B<&P@.F9A8V4V(#MI
|
|
M1J!03U.@5$A%3J!++58Q6J`^H#`-#2!L9&$@(R0P,2`[;U1(15)725-%+*!$
|
|
M4D%7H%1(10T@<W1A(&9A8V5S(#M&04-%(0T-(&QD82!P,7@-('-T82!T>#$-
|
|
M(&QD82!P,7D-('-T82!T>3$-(&QD82!P,G@-('-T82!T>#(-(&QD82!P,GD-
|
|
M('-T82!T>3(-(&IS<B!D<F%W(#MP,2UP,@T-(&QD82!P,W@-('-T82!T>#$-
|
|
M(&QD82!P,WD-('-T82!T>3$-(&IS<B!D<F%W(#MP,BUP,PT-(&QD82!P-'@-
|
|
M('-T82!T>#(-(&QD82!P-'D-('-T82!T>3(-(&IS<B!D<F%W(#MP,RUP-`T-
|
|
M(&QD82!P,7@-('-T82!T>#$-(&QD82!P,7D-('-T82!T>3$-(&IS<B!D<F%W
|
|
M(#MP-"UP,:"@9D%#1:`QH$1/3D4N#2!J;7`@.F9A8V4R(#MI1J!/3D6@25.@
|
|
M5DE324),12R@5$A%H$]42$52#2`@(#M)4TXG5"X-.F9A8V4V(&QD82!K#2!S
|
|
M96,-('-B8R!P-7H-(&)V<R`Z9F%C93(-(&-L8PT@861C('`Q>B`[;D]7H$-(
|
|
M14-+H$E&H&LM5C9:H#R@,`T@8G9C(#ID<F%W-B`[;$]61:!42$%4H$]615)&
|
|
M3$]7#2!L9&$@<#%Z#3ID<F%W-B!B<&P@.F9A8V4R(#MI1J!.3U0LH$=/H$].
|
|
M#0T@;&1A(",D,C`-('-T82!F86-E<R`[;U1(15)725-%+*!$4D%7H$E4#0T@
|
|
M;&1A('`U>`T@<W1A('1X,@T@;&1A('`U>0T@<W1A('1Y,@T@;&1A('`V>`T@
|
|
M<W1A('1X,0T@;&1A('`V>0T@<W1A('1Y,0T@:G-R(&1R87<@.W`U+7`V#0T@
|
|
M;&1A('`W>`T@<W1A('1X,@T@;&1A('`W>0T@<W1A('1Y,@T@:G-R(&1R87<@
|
|
M.W`V+7`W#0T@;&1A('`X>`T@<W1A('1X,0T@;&1A('`X>0T@<W1A('1Y,0T@
|
|
M:G-R(&1R87<@.W`W+7`X#0T@;&1A('`U>`T@<W1A('1X,@T@;&1A('`U>0T@
|
|
M<W1A('1Y,@T@:G-R(&1R87<@.W`X+7`U#0TZ9F%C93(@;&1A(&L-('-E8PT@
|
|
M<V)C('`Q>@T@8G9S(#IF86-E-0T@8VQC#2!A9&,@<#1Z(#MK+58R6J`\H#`_
|
|
M#2!B=F,@.F1R87<R#2!L9&$@<#1Z#3ID<F%W,B!B<&P@.F9A8V4U#2!L9&$@
|
|
M(R0P,B`[:4:@4T\LH$1205>@250A#2!O<F$@9F%C97,-('-T82!F86-E<PT-
|
|
M(&QD>"!P,7@@.W=%)U)%H$1/24Y'H%1(25.@5$A)4Z!705D-('-T>"!T>#$@
|
|
M.U1/H%-!5D6@0:!&15>@0UE#3$53#2!L9'@@<#%Y#2!S='@@='DQ#0T@86YD
|
|
M(",D,#$@.W-(05)%4Z!!3J!%1$=%H%=)5$B@1D%#1:`Q#2!B;F4@.F8R<S(@
|
|
M.W-+25"@5$^@3D585*!%1$=%H$E&H%!215-%3E0-#2!L9&$@<#)X#2!S=&$@
|
|
M='@R#2!L9&$@<#)Y#2!S=&$@='DR#2!J<W(@9')A=R`[<#$M<#(-#3IF,G,R
|
|
M(&QD>"!P-7@-('-T>"!T>#(-(&QD>"!P-7D-('-T>"!T>3(-(&IS<B!D<F%W
|
|
M(#MP,2UP-0T-(&QD>"!P-G@-('-T>"!T>#$-(&QD>"!P-GD-('-T>"!T>3$-
|
|
M#2!L9&$@9F%C97,-(&%N9"`C)#(P(#MA3%-/H%-(05)%4Z!!3J!%1$=%H%=)
|
|
M5$B@-@T@8FYE(#IF,G,T#0T@:G-R(&1R87<@.W`U+7`V#0TZ9C)S-"!L9&$@
|
|
M<#)X#2!S=&$@='@R#2!L9&$@<#)Y#2!S=&$@='DR(#MS54-(H$E3H$9!0T6@
|
|
M,@T@:G-R(&1R87<@.W`V+7`R#2!J;7`@.F9A8V4S(#MS2TE0H#4-#3IF86-E
|
|
M-2!L9&$@:PT@<V5C#2!S8F,@<#1Z#2!B=G,@.F9A8V4S#2!C;&,-(&%D8R!P
|
|
M,7H@.W-!346@5$A)3D>@04=!24XN+BX-(&)V8R`Z9')A=S4-(&QD82!P,7H-
|
|
M.F1R87<U(&)P;"`Z9F%C93,-(&QD82`C)#$P#2!O<F$@9F%C97,-('-T82!F
|
|
M86-E<PT-(&QD>"!P,W@-('-T>"!T>#$-(&QD>"!P,WD-('-T>"!T>3$-#2!A
|
|
M;F0@(R0P,2`[<TA!4D53H%=)5$B@,0T@8FYE(#IF-7,R#0T@;&1A('`T>`T@
|
|
M<W1A('1X,@T@;&1A('`T>0T@<W1A('1Y,@T@:G-R(&1R87<@.W`S+7`T#0TZ
|
|
M9C5S,B!L9&$@<#=X#2!S=&$@='@R#2!L9&$@<#=Y#2!S=&$@='DR#2!J<W(@
|
|
M9')A=R`[<#,M<#<-#2!L9&$@<#AX#2!S=&$@='@Q#2!L9&$@<#AY#2!S=&$@
|
|
M='DQ#0T@;&1A(&9A8V5S#2!A;F0@(R0R,"`[<TA!4D53H%=)5$B@-@T@8FYE
|
|
M(#IF-7,T#0T@:G-R(&1R87<@.W`W+7`X#3IF-7,T(&QD82!P-'@-('-T82!T
|
|
M>#(-(&QD82!P-'D-('-T82!T>3(@.W`X+7`T#2!J<W(@9')A=R`[=%=/H$U/
|
|
M4D6@5$^@1T\A#0TZ9F%C93,@;&1A(&L-('-E8PT@<V)C('`Q>@T@8G9S(#IF
|
|
M86-E-`T@8VQC#2!A9&,@<#)Z#2!B=F,@.F1R87<S#2!L9&$@<#)Z#3ID<F%W
|
|
M,R!B<&P@.F9A8V4T(#MA2*!214-+3TZ@250G4Z!!)TA)1$1%3BR@6550#2!L
|
|
M9&$@(R0P-`T@;W)A(&9A8V5S#2!S=&$@9F%C97,-#2!L9'@@<#%X#2!S='@@
|
|
M='@Q#2!L9'@@<#%Y#2!S='@@='DQ#0T@86YD(",D,#$@.W-(05)%4Z!7251(
|
|
MH#$-(&)N92`Z9C-S,@T-(&QD82!P-'@-('-T82!T>#(-(&QD82!P-'D-('-T
|
|
M82!T>3(-(&IS<B!D<F%W(#MP,2UP-`T-.F8S<S(@;&1X('`U>`T@<W1X('1X
|
|
M,@T@;&1X('`U>0T@<W1X('1Y,@T-(&QD82!F86-E<PT@86YD(",D,#(@.W-(
|
|
M05)%4Z!7251(H#(-(&)N92`Z9C-S,PT-(&IS<B!D<F%W(#MP,2UP-0TZ9C-S
|
|
M,R!L9'@@<#AX#2!S='@@='@Q#2!L9'@@<#AY#2!S='@@='DQ#0T@;&1A(&9A
|
|
M8V5S#2!A;F0@(R0R,"`[<TA!4D53H%=)5$B@-@T@8FYE(#IF,W,T#0T@:G-R
|
|
M(&1R87<@.W`U+7`X#3IF,W,T(&QD>"!P-'@-('-T>"!T>#(-(&QD>"!P-'D-
|
|
M('-T>"!T>3(-#2!L9&$@9F%C97,-(&%N9"`C)#$P(#MS2$%215.@5TE42*`U
|
|
M#2!B;F4@9F%C961O;F4-#2!J<W(@9')A=R`[<#@M<#0-(&IM<"!F86-E9&]N
|
|
M90T-.F9A8V4T(&QD82!K#2!S96,-('-B8R!P,GH-(&)V<R!F86-E9&]N90T@
|
|
M8VQC#2!A9&,@<#%Z#2!B=F,@.F1R87<T#2!L9&$@<#%Z#3ID<F%W-"!B<&P@
|
|
M9F%C961O;F4-#2!L9&$@<#)X#2!S=&$@='@Q#2!L9&$@<#)Y#2!S=&$@='DQ
|
|
M#0T@;&1A(&9A8V5S#2!A;F0@(R0P,2`[<TA!4D53H%=)5$B@,0T@8FYE(#IF
|
|
M-',R#0T@;&1A('`S>`T@<W1A('1X,@T@;&1A('`S>0T@<W1A('1Y,@T@:G-R
|
|
M(&1R87<@.W`R+7`S#0TZ9C1S,B!L9&$@<#9X#2!S=&$@='@R#2!L9&$@<#9Y
|
|
M#2!S=&$@='DR#0T@;&1A(&9A8V5S#2!A;F0@(R0P,B`[<TA!4D53H%=)5$B@
|
|
M,@T@8FYE(#IF-',S#0T@:G-R(&1R87<@.W`R+7`V#3IF-',S(&QD82!P-W@-
|
|
M('-T82!T>#$-(&QD82!P-WD-('-T82!T>3$-#2!L9&$@9F%C97,-(&%N9"`C
|
|
M)#(P(#MS2$%215.@5TE42*`V#2!B;F4@.F8T<S0-#2!J<W(@9')A=R`[<#8M
|
|
M<#<-.F8T<S0@;&1A('`S>`T@<W1A('1X,@T@;&1A('`S>0T@<W1A('1Y,@T-
|
|
M(&QD82!F86-E<PT@86YD(",D,3`@.W-(05)%4Z!7251(H#4-(&)N92!F86-E
|
|
M9&]N90T-(&IS<B!D<F%W(#MP-RUP,PUF86-E9&]N92`@(#MW2$57(:"@=$E-
|
|
M1:!&3U*@0:!"1452+@T-*BHJ*J!N3U>@5T6@3D5%1*!43Z!53D9)3$R@5$A%
|
|
MH$]55%-)1$6@1E)/3:!42$6@1D%#15,-=6YF:6QL(&QD>2!Y;6EN#3IL;V]P
|
|
M(#X^/B!S971B=68-(&QD>"`C,#@-.FPQ(&QD82`H8G5F9F5R*2QY#2!E;W(@
|
|
M(R1F9B`[9T^@5$E,3*!71:!&24Y$H$&@4$Q/5%1%1`T@8FYE(#IG;W1C:&$@
|
|
M.U!/24Y4H"A)+D4NH&&@/#Z@)&9F*0TJH&QD8:`C,#"@.W5.1DE,3$E.1Z!!
|
|
M4Z!71:!'3RXN+@T@<W1A("AB=69F97(I+'D-(&QD82`C)#@P#2!S=&$@8G5F
|
|
M9F5R#2!L9&$@*&)U9F9E<BDL>0T@96]R(",D9F8-(&)N92`Z9V]T8VAA#2J@
|
|
M;&1AH",P,`T@<W1A("AB=69F97(I+'D-('-T82!B=69F97(-(&EN8R!B=69F
|
|
M97(K,0T@9&5X("`[=$A)4Z!)4Z!/55*@4T%&1519H%9!3%9%#2!B;F4@.FPQ
|
|
M(#MR14%,3%F@4TA/54Q$3B=4H$Y%142@250-(&IS<B!C:&]K90T@:FUP('-W
|
|
M87!B=68-#3IG;W1C:&$@.V&@0T].5$%)3E.@5$A%H&5O<J!03$]4H%9!3%5%
|
|
M#2!S=&$@=&5M<#$@.VY/5Z!&24Y$H%1(1:!(24=(H$))5`T@;&1A(",P,`TZ
|
|
M;#(@<V5C#2!R;VP-(&QS<B!T96UP,2`[<TA/54Q$H%)%04Q,6:!54T6@0:!4
|
|
M04),10T@8FYE(#IL,B`[1D]2H%1(25,A#2!A;F0@*&)U9F9E<BDL>0T@<W1A
|
|
M("AB=69F97(I+'D-#2!L9&$@>G1E;7`@.VY/5Z!'3Z!43Z!42$6@14Y$#2`@
|
|
M(#MC05)26:!)4Z!#3$5!4@T@("`[84-454%,3%F@5T6@041$H#<-(&%D8R`C
|
|
M)#`V(#LQ-J!#3TQ534Y3H$]&H#$R.*!"651%4PT@<W1A(&)U9F9E<BLQ#2!L
|
|
M9&$@(R0X,`T@<W1A(&)U9F9E<@TZ;&]O<#(@;&1A("AB=69F97(I+'D@.V%.
|
|
M1*!73U)+H$)!0TM705)$4R$-(&5O<B`C)&9F#2!B;F4@.F=O=&-H83(-('-T
|
|
M82`H8G5F9F5R*2QY#2!S=&$@8G5F9F5R(#MS5$E#2Z!!H%I%4D^@24Y43Z!"
|
|
M549&15(-(&QD82`H8G5F9F5R*2QY#2!E;W(@(R1F9@T@8FYE(#IG;W1C:&$R
|
|
M#2!S=&$@*&)U9F9E<BDL>0T@;&1A(",D.#`-('-T82!B=69F97(-(&1E8R!B
|
|
M=69F97(K,0T@8FYE(#IL;V]P,@T-.F=O=&-H83(@<W1A('1E;7`Q(#MA1T%)
|
|
M3J!&24Y$H%1(1:!(24=(H$))5`T@;&1A(",P,`TZ;#,@<V5C#2!R;W(-(&%S
|
|
M;"!T96UP,0T@8FYE(#IL,PT@86YD("AB=69F97(I+'D-('-T82`H8G5F9F5R
|
|
M*2QY#0T@:6YY("`[;D]7H$M%15"@1T])3D<-(&-P>2!Y;6%X#2!B8V,@.FQO
|
|
M;W`@.W5.5$E,H%=%H$A)5*!934%8(0T@8F5Q(#IL;V]P(#MW1:!.145$H%1(
|
|
M1:!,05-4H$].1:!43T\N#0TJ*BHJH'-705"@0E5&1D524PT-<W=A<&)U9B!L
|
|
M9&$@=FUC<V(-(&5O<B`C)#`R(#MP4D545%F@5%))0TM9+*!%2#\-('-T82!V
|
|
M;6-S8@T@;&1A(",D,#@-(&5O<B!Z=&5M<"`[6E1%35`]2$E'2*!"651%H$I5
|
|
M4U2@1DQ)4%,-('-T82!Z=&5M<"`[0D545T5%3J`D,S"@04Y$H"0S.`T-(&IM
|
|
M<"!M86EN(#MA4D]53D2@04Y$H$%23U5.1*!71:!'3RXN+@T-('1X="`G<T%-
|
|
M1:!42$E.1Z!71:!$3Z!%5D526:!.24=(5"R@<$E.2UDZH"<-('1X="`G5%)9
|
|
MH%1/H%1!2T6@3U9%4J!42$6@5T]23$0A)PT-#2HM+2TM+2TM+2TM+2TM+2TM
|
|
M+2TM+2TM+2TM+2TM+2TM#2J@9T5.15)!3*!154535$E/3D%"3$4M5D%,546@
|
|
M15)23U*@4%)/0T5$55)%#0UC:&]K92!L9'@@(S`P#3IL;V]P(&QD82`Z8W1E
|
|
M>'0L>`T@8F5Q(#ID;VYE#2!J<W(@8VAR;W5T#2!I;G@-(&IM<"`Z;&]O<`TZ
|
|
M9&]N92!R=',-.F-T97AT(&AE>"`P9"`[8W(-('1X="`G4T]-151(24Y'H$-(
|
|
M3TM%1*`Z*"<-(&AE>"`P9#`P#0T@='AT("=N05)&(2<-#2HM+2TM+2TM+2TM
|
|
M+2TM+2TM+2TM+2TM+2TM+2TM+2TM#2J@9%)!5TE.)Z!!H$Q)3D4NH*!AH$9!
|
|
M2$Z@3$%(3BX-#2HJ*J!S3TU%H%5314953*!-04-23U,-#7!L;W1P>"!M86,@
|
|
M(#M03$]4H$&@4$])3E2@24Z@6`T@<&AA("`[=5-%H%1(25.@3TY%H$5615)9
|
|
MH%1)344-(&QD82!B:71P+'@@.WB@25.@24Y#4D5!4T5$#2!B;6D@8S$-(&QD
|
|
M82`C)#@P(#MT04),1:!(05.@0D5%3J!214%24D%.1T5$#2!E;W(@8G5F9F5R
|
|
M(#M&3U*@1DE,3$E.1Z!&04-%4PT@<W1A(&)U9F9E<@T@8FUI(&,R#2!I;F,@
|
|
M8G5F9F5R*S$-8S(@;&1A(",E,#$Q,3$Q,3$@.VY/5$6@5$A!5*!42$E3H$E3
|
|
MH$-(04Y'140-8S$@86YD("AB=69F97(I+'D@.T9/4J!03$]45$E.1Z!&24Q,
|
|
M142@1D%#15,-('-T82`H8G5F9F5R*2QY#2!P;&$@(#MN145$H%1/H%-!5D6@
|
|
M82$-(#P\/`T-<&QO='!Y(&UA8R`@.W!,3U2@0:!03TE.5*!)3J!9.J!324U0
|
|
M3$52H$%.1*!.14-%4U-!4EDA#2!P:&$@(#MU4T6@5$A)4Z!/3D6@5TA%3J!9
|
|
M3U6@2E535*!)3D-214%31:!Y#2!L9&$@8FET<"QX(#M"552@>*!$3T533B=4
|
|
MH$-(04Y'10T@86YD("AB=69F97(I+'D-('-T82`H8G5F9F5R*2QY#2!P;&$-
|
|
M(#P\/`T-8VEN:70@;6%C("`[;4%#4D^@5$^@24Y)5$E!3$E:1:!42$6@0T]5
|
|
M3E1%4@T@;&1A(%TQ(#M$6*!/4J!$60T@;'-R#2!E;W(@(R1F9B`[*&Y/5*!2
|
|
M14%,3%F@5%=/)U.@0T]-4$Q%345.5"D-(&%D8R`C)#`Q(#MAH#V@,C4V+418
|
|
M+S*@3U*@,C4V+419+S(-(#P\/"`@.W1(1:!$6"\RH$U!2T53H$&@3DE#15*@
|
|
M3$]/2TE.1Z!,24Y%#0UX<W1E<"!M86,@(#MM04-23Z!43Z!404M%H$&@4U1%
|
|
M4*!)3J!X#7AL;V]P(&EN>`T@861C(&1Y#2!B8V,@;#$-*J!D3Z!71:!54T6@
|
|
M:6YYH$]2H&1E>:!(15)%/PT@:68@:2Q=,2`[:4:@5$A%H$9)4E-4H$-(05)!
|
|
M0U1%4J!)4Z!!3J`G:2<-(&EN>0T@96QS90T@9&5Y#2!F:6X-('-B8R!D>`UL
|
|
M,2`^/CX@<&QO='!X(#MA3%=!65.@5$%+1:!!H%-415"@24Z@>`T@8W!X('@R
|
|
M#2!B;F4@>&QO;W`-(#P\/`T->7-T97`@;6%C("`[<T%-1:!42$E.1RR@0E54
|
|
MH$9/4J!Y#7EL;V]P(&EF(&DL73$-(&EN>0T@96QS90T@9&5Y#2!C;&,@(#MV
|
|
M15)9H$E-4$]25$%.5"$-(&9I;@T@861C(&1X#2!B8V,@;#(-(&EN>"`@.V%,
|
|
M5T%94Z!)3D-214%31:!X#2!S8F,@9'D-(#X^/B!P;&]T<'@-(&IM<"!L,PUL
|
|
M,B`^/CX@<&QO='!Y(#MW1:!/3DQ9H$E.0U)%05-%1*!Y#6PS(&-P>2!Y,@T@
|
|
M8FYE('EL;V]P#2`\/#P-#2HJ*BJ@:4Y)5$E!3*!,24Y%H%-%5%50#0UD<F%W
|
|
M(#X^/B!M;W9E+'1X,3MX,2`@.VU/5D6@4U151D:@24Y43Z!:15)/H%!!1T4-
|
|
M(#X^/B!M;W9E+'1X,CMX,B`@.W=(15)%H$E4H$-!3J!"1:!-3T1)1DE%1`T@
|
|
M/CX^(&UO=F4L='DQ.WDQ#2`^/CX@;6]V92QT>3([>3(-(#X^/B!S971B=68@
|
|
M.VY/5Z!71:!#04Z@0TQ/0D)%4J!42$6@0E5&1D52#0T@<V5C("`[;4%+1:!3
|
|
M55)%H%@Q/%@R#2!L9&$@>#(-('-B8R!X,0T@8F-S(#IC;VYT#2!L9&$@>3(@
|
|
M.VE&H$Y/5"R@4U=!4*!P,:!!3D2@<#(-(&QD>2!Y,0T@<W1A('DQ#2!S='D@
|
|
M>3(-(&QD82!X,0T@;&1Y('@R#2!S='D@>#$-('-T82!X,@T-('-E8PT@<V)C
|
|
M('@Q(#MN3U>@83U$6`TZ8V]N="!S=&$@9'@-(&QD>"!X,2`[<%54H%@QH$E.
|
|
M5$^@>"R@3D]7H%=%H$-!3J!44D%32*!X,0T-8V]L=6UN(&QD82!X,2`[9DE.
|
|
M1*!42$6@1DE24U2@0T],54U.H$9/4J!X#2!L<W(@(#LH=$A)4Z!#04Z@0D6@
|
|
M34%$1:!-54-(H$9!4U1%4B$I#2!L<W(@(#MT2$521:!!4D6@6#$O.*`Q,CB@
|
|
M0EE41:!"3$]#2U,-(&QS<B`@.W=(24-(H$U%04Y3H%@Q+S$VH#(U-J!"651%
|
|
MH$),3T-+4PT@;'-R#2!B8V,@.F5V96X@.W=)5$B@0:!03U-324),1:!%6%12
|
|
M0:`Q,CB@0EE41:!"3$]#2PT@;&1Y(",D.#`@.TE&H%-/+*!3152@5$A%H$A)
|
|
M1TB@0DE4#2!S='D@8G5F9F5R#2!C;&,-.F5V96X@861C(&)U9F9E<BLQ(#MA
|
|
M1$2@24Z@5$A%H$Y534)%4J!/1J`R-3:@0EE41:!"3$]#2U,-('-T82!B=69F
|
|
M97(K,2`[84Y$H%-43U)%H$E4(0T-('-E8PT@;&1A('DR(#MC04Q#54Q!5$6@
|
|
M1%D-('-B8R!Y,0T@8F-S(#IC;VYT,B`[:5.@63(^63$_#2!E;W(@(R1F9B`[
|
|
M;U1(15)725-%H$19/5DQ+5DR#2!A9&,@(R0P,0TZ8V]N=#(@<W1A(&1Y#2!C
|
|
M;7`@9'@@.W=(3R=3H$))1T=%4CJ@1%F@3U*@1%@_#2!B8W,@<W1E<&EN>2`[
|
|
M:4:@1%DLH%=%H$Y%142@5$^@5$%+1:!"24>@4U1%4%.@24Z@60T-<W1E<&EN
|
|
M>"!L9'D@>3$@.WB@25.@04Q214%$6:!3152@5$^@6#$-(&QD82!B:71P+'@@
|
|
M.W!,3U2@5$A%H$9)4E-4H%!/24Y4#2J@96]RH",D9F8-(&%N9"`H8G5F9F5R
|
|
M*2QY#2!S=&$@*&)U9F9E<BDL>0T@/CX^(&-I;FET+&1X(#MI3DE424%,25I%
|
|
MH%1(1:!#3U5.5$52#2!C<'D@>3(-(&)C<R!X9&5C>2`[9$^@5T6@4U1%4*!&
|
|
M3U)705)$4Z!/4J!"04-+5T%21%.@24Z@>3\-#7AI;F-Y(#X^/B!X<W1E<"QI
|
|
M;GD-(')T<PT-<W1E<&EN>2!L9'D@>3$@.W=%3$PLH$&@3$E45$Q%H%)%4$54
|
|
M251)3TZ@3D5615*@2%525*!!3EE/3D4-(&QD82!B:71P+'@-*J!E;W*@(R1F
|
|
M9@T@86YD("AB=69F97(I+'D-('-T82`H8G5F9F5R*2QY#2`^/CX@8VEN:70L
|
|
M9'D-(&-P>2!Y,@T@8F-S('ED96-Y#0UY:6YC>2`^/CX@>7-T97`L:6YY#2!R
|
|
M=',-#7AD96-Y(#X^/B!X<W1E<"QD97D@.W1(25.@25.@4%54H$A%4D6@4T^@
|
|
M5$A!5`T@<G1S("`[8E)!3D-(15.@05)%H$Q%1T%,#0UY9&5C>2`^/CX@>7-T
|
|
M97`L9&5Y#2!R=',-#0TJ+2TM+2TM+2TM+2TM+2TM+2TM+2TM+2TM+2TM+2TM
|
|
M+0TJH&-,14%.H%50#0UC;&5A;G5P(&QD82!V;6-S8B`[<U=)5$-(H$-(05*@
|
|
M4D]-H$)!0TN@24X-(&%N9"`C)3$Q,3$P,3`Q(#M$149!54Q4#2!S=&$@=FUC
|
|
M<V(-#2!R=',@(#M"644A#0T@='AT("=H05!06:!H3TQ)1$%94R&@)PT@='AT
|
|
M("=33$J@,3(O.30G#0TJ+2TM+2TM+2TM+2TM+2TM+2TM+2TM+2TM+2TM+2TM
|
|
M+0TJH'-%5*!54*!"252@5$%"3$4-#2!D<R!>(#MC3$5!4J!43Z!%3D2@3T:@
|
|
M4$%'10T@("`[<T^@5$A!5*!404),15.@4U1!4E2@3TZ@0:!004=%H$)/54Y$
|
|
M05)9#6)I='`@;'5P(#$V(#LQ,CB@94Y44DE%4Z!&3U*@>`T@9&9B("4P,3$Q
|
|
M,3$Q,0T@9&9B("4Q,#$Q,3$Q,0T@9&9B("4Q,3`Q,3$Q,0T@9&9B("4Q,3$P
|
|
M,3$Q,0T@9&9B("4Q,3$Q,#$Q,0T@9&9B("4Q,3$Q,3`Q,0T@9&9B("4Q,3$Q
|
|
M,3$P,0T@9&9B("4Q,3$Q,3$Q,`T@+2U>#0US:6X@.W1!0DQ%H$]&H%-)3D53
|
|
M+*`Q,C"@0EE415,-8V]S(&5Q=2!S:6XK,3(X(#MT04),1:!/1J!#3U-)3D53
|
|
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
|
|
M(@#D#VD!F2)%34%)3"!32E5$1$!.5U4N1415("(`!A!J`9DB("U/4BT!""8(
|
|
M"@"9(I,%3D]415,@1D]2($-50D4S1#(N,"!!3D0@,BXQ(@!-"!0`F2(13$]!
|
|
M1"!42$4@+D\@3T)*14-4($9)3$4@0D5&3U)%(@!F"!X`F2),3T%$24Y'($E.
|
|
M250S1#(N,"(`C@@H`)DB5$A%(%)%3$5604Y4($Q)3D53($E.($E.250S1"!!
|
|
M4D4B`+`(,@"9(DQ)3D53(#$P,"TQ-3`@04Y$(#(S,RTS,3`N(@#1"#P`F2)$
|
|
M($%.1"!:,"!!4D4@24X@3$E.12`R-#`N(@#\"$8`F2(1248@64]5(%1262!4
|
|
M3R!#2$%.1T4@4T]-151(24Y'($%.1"(`)PE0`)DB1T54($%.(#])3$Q%1T%,
|
|
M(%%504Y42519($524D]2($I54U0B`#@)6@"9(E194$4@0TQ2+B(`9`ED`)DB
|
|
M$1*>248@64]5(%=!3E0@5$\@4E5.($-50D4S1#(N,2!-04M%(@","6X`F2(2
|
|
M4U5212!43R!#2$%.1T4@1"!)3B!,24Y%(#(T,"!43R(`I`EX`)DB$D0].#4@
|
|
M050@34]35"$A(2(`T@F"`)DB$0533TU%($]&(%1(12!:15)/(%!!1T4@3$]#
|
|
M051)3TY3(%53140B```*C`"9(DU!62!(3U-%($)!4TE#("TM(%)%0D]/5"!"
|
|
M149/4D4@4T%624Y'(@`4"I8`F2)!3ED@0TA!3D=%4RXB`$,*H`"9(A%+1450
|
|
M($E.($U)3D0@5$A!5"!93U4@0T%.(%!215-3($8Q($540R(`<@JJ`)DB355,
|
|
M5$E03$4@5$E-15,@5$\@4U!%140@55`@5$A%(%)/5$%424].(@","K0`F2(1
|
|
M$2A04D534R!!3ED@2T59*2([`)\*O@"A020ZBT$DLB(BIS$Y,`#,"L@`F2*3
|
|
M0U5"13-$,BXQ($9%05154D53($U53%1)0T],3U(@04Y$($%.(@#V"M(`F2)!
|
|
M4%!!3$Q)3D=,62!44DE624%,(%1%6%154D4@34%0($]&(@`A"]P`F2)!(%-/
|
|
M4E0@+2T@250@5T%3($A!0TM%1"!43T=%5$A%4B!!5"(`30OF`)DB5$A%($Q!
|
|
M4U0@34E.551%+"!!3D0@5TE,3"!"12!44D5!5$5$(@!R"_``F2))3B!-3U)%
|
|
M($1%5$%)3"!)3B!42$4@1E5455)%+B(`G@OZ`)DB$5=%($A/4$4@64]5($Q)
|
|
M2T4@5$A%4T4@4%)/1U)!35,@04Y$(@"W"P0!F2)&24Y$(%1(14T@55-%1E5,
|
|
M(2(````:&AH:&AH:&AH:&AH:&AH:&AH:&AH:&AH:&AH:&AH:&AH:&AH:&AH:
|
|
6&AH:&AH:&AH:&AH:&AH:&AH:&AH:&AH:
|
|
`
|
|
end
|
|
|
|
==============================================================================
|
|
2D Graphics Toolbox -- Circles
|
|
by Stephen Judd (sjudd@nwu.edu)
|
|
|
|
3D is fun and interesting but in the end we must always display our work on a
|
|
two-dimensional surface, the CRT. Not only are two-dimensional algorithms
|
|
the foundation of three-dimensional drawings, they are of course useful for
|
|
many other applications. This new series of articles is intended to
|
|
complement (hah -- get it?) the 3D articles by George and myself. Between
|
|
the two articles you should have at your disposal a powerful graphics toolbox
|
|
for all of your applications.
|
|
|
|
The foundation of all of our drawings is a single point, and a logical next
|
|
step would be a line. Algorithms for doing both of these things were
|
|
discussed in depth in the first article of the 3D series, so you can look
|
|
those up in a back-issue of C=Hacking. What is next after points and lines?
|
|
Curves! So to start with, let's think about drawing a circle.
|
|
|
|
You can write the equation for a circle in many ways, depending on your
|
|
coordinate system. In cartesian coordinates, the equation of a circle is:
|
|
|
|
x^2 + y^2 = r^2 (1)
|
|
|
|
This is a circle centered at the origin (on a computer we can always
|
|
translate the points wherever we want), with radius r. In polar coordinates
|
|
the equation is r=const. We can write down trigonometric relations out the
|
|
wazoo, too, but no matter what it looks like we're going to have some
|
|
complicated math ahead of us involving multiplications and worse, i.e.
|
|
time-intensive computations. Perhaps the simplest of these would be:
|
|
|
|
x=r*cos(theta)
|
|
y=r*sin(theta)
|
|
|
|
where theta runs from zero to 2*pi. If we have a table of sines and cosines,
|
|
and use the fast multiplication algorithm given in this months 3D article, we
|
|
have a routine which gives a good circle in around 50 cycles or so per pixel,
|
|
sans plot.
|
|
|
|
But let's step back and consider: the above equations all give a "perfect"
|
|
circle. Is there any way we can draw an "approximate" circle, with a large
|
|
speed increase? The answer is of course yes, since this wouldn't be a very
|
|
interesting article otherwise!
|
|
|
|
Let's say we are standing at a point on the circle, and want to take a step
|
|
towards the next point. In what direction do we take this step? Consider a
|
|
line tangent to the circle, touching the point where we are standing. If we
|
|
take a little step in that direction, we ought to get close to the next point
|
|
on the circle. The way to calculate the tangent line is to use the
|
|
derivative, which will give us the slope of the tangent. Taking
|
|
differentials of equation (1) above we have
|
|
|
|
2*x dx + 2*y dy = 0
|
|
or
|
|
dy/dx = -x/y
|
|
|
|
Now, if I am at a point (x,y), I want to take a little step in x and a little
|
|
step in y:
|
|
|
|
x = x+dx
|
|
y = y+dy
|
|
|
|
but from the first equation we know that dx = -y/x * dy so that at each step
|
|
our iteration is
|
|
|
|
y = y + dy
|
|
x = x - dy*(y/x)
|
|
|
|
This is the basis of our algorithm. You could also of course use
|
|
|
|
x = x + dx
|
|
y = y - dx*(x/y)
|
|
|
|
(this is called Euler's method for solving a differential equation, and these
|
|
concepts are fundamental to most algorithms for solving differential
|
|
equations numerically).
|
|
|
|
Let's start at the point x=r,y=0 i.e. the right-endpoint of the circle.
|
|
Since we are on a computer we want to take a step of length one in some
|
|
direction (i.e. step one pixel), and at this point on the circle y is clearly
|
|
increasing much faster than x. You may remember from the line drawing
|
|
algorithm that we viewed the process as "keep taking steps in x until it is
|
|
time to take a step in y". We are going to apply that same philosophy here:
|
|
keep taking steps in y until it is time to take a step (backwards) in x. So
|
|
our iteration is now:
|
|
|
|
y_{n+1} = y_n + 1
|
|
x_{n+1} = x_n - y_n/x_n
|
|
|
|
Hmmm... we have this little problem now of y/x. I certainly don't want to
|
|
deal with floating point. How do we get around this? The trick is to think
|
|
of x as a _discrete_ variable -- as far as a pixel is concerned x is just an
|
|
integer, and has no floating point part. So we are going to treat x as a
|
|
constant, until it is time to decrease x by one! When does this happen?
|
|
Let's expand the x-iteration:
|
|
|
|
x_{n+1} = x_n - y_n/x_n
|
|
= (x_{n-1} - y_{n-1}/x_{n-1}) - y_n/x_n
|
|
|
|
This is where the magic of using a discrete x comes in. If we make this
|
|
assumption of constant x, then x_{n-1} = x_n and the above iteration becomes
|
|
|
|
x_{n+1} = x_{n-1} - (y_{n-1} + y_n)/x_{n-1}
|
|
|
|
If we continue this process over an interval where x is constant we get
|
|
|
|
x_{n+1} = x_0 - (y_n + y_{n-1} + y_{n-2} + ...)/x_0
|
|
|
|
When is it time to decrease x? When the sum of the y values at each
|
|
iteration exceeds the current x-value the fraction above will be greater than
|
|
one, and we will then decrease x. Like I said, we keep x constant, until it
|
|
is time to decrease it! :)
|
|
|
|
How long do we do this for? In the same way that at x=r y increases much
|
|
faster than x does, when y=r x increases much faster than y does. Somehwere
|
|
in-between they have to be increasing at the same rate, which means the slope
|
|
of the tangent line is equal to +/-1, i.e. at the point x=y. At this point
|
|
we have drawn one-eighth of the circle. We can either draw all eight
|
|
segments of the circle independentally, or else we can use the symmetry of a
|
|
circle and do an 8-way plot.
|
|
|
|
TO SUMMARIZE: Here is the basic algorithm
|
|
|
|
x=r
|
|
y=0
|
|
a=0
|
|
:loop
|
|
y=y+1
|
|
a=a+y
|
|
if a>x then x=x-1:a=a-x
|
|
plot8(x,y)
|
|
:until x<=y
|
|
|
|
Of course you can refine this in several ways, which will speed things up in
|
|
assembly. For instance, if instead of a=0 we start with a=x, then the logic
|
|
becomes
|
|
|
|
a=a-y
|
|
if a<0 then x=x-1:a=a+x
|
|
|
|
To do the a=a-x you could use a table where f(x)=x, or you might try
|
|
something else; here is some code in assembly:
|
|
|
|
LDX R
|
|
LDY #00
|
|
STY Y
|
|
TXA
|
|
:loop JSR PLOT8 ;Eight-way symmetric plot
|
|
SEC ;Might not need this depending on PLOT8
|
|
INC Y ;Y is in zero page
|
|
SBC Y
|
|
BCS :loop
|
|
DEX
|
|
STX X ;X is also in zero-page
|
|
ADC X ;Carry is already clear
|
|
CPX Y
|
|
BCS :loop
|
|
JSR PLOT8 ;Catch the last point
|
|
|
|
Starting at :loop we have 2+3+3+3=11 cycles in the best case and
|
|
2+3+3+2+2+3+3+3+3=24 cycles in the worst case, excluding PLOT8. You can, of
|
|
course, change the program logic around; if PLOT8 returned with the carry
|
|
always clear it would be much smarter to start A as A=256-A and use A=A+Y at
|
|
each step instead of A=A-Y. Christopher Jam (phillips@ee.uwa.edu.au)
|
|
suggested starting at X=Y(=R/sqrt(2)) so that the CPX Y instruction could be
|
|
removed (I don't know how this affects accuracy, though).
|
|
|
|
"Yeah, but how well does it work?" Quite well, as a matter of fact. It will
|
|
draw a perfect circle for circles with a radius greater than twelve or so.
|
|
For circles with a smaller radius the sides start to flatten out, and the
|
|
circle becomes squareish. Interestingly, the "discrete x" approximation
|
|
improves the result over the straight floating-point calculation
|
|
significantly!
|
|
|
|
So this is the best algorithm I was able to come up with for drawing a
|
|
circle. If you have any suggestions for improvements or other ideas, please
|
|
feel free to share them :). As always I must thank George Taylor and
|
|
Christopher Jam for their suggestions and for helping me work out some ideas.
|
|
|
|
If you have any particular 2D algortihms/calculations that you would like to
|
|
see, please feel free to suggest future topics for the 2D graphics toolbox.
|
|
|
|
Finally, here is a BASIC7.0 program which demonstrates the algorithm:
|
|
|
|
0 REM FAST CIRCLE -- SLJ 9/94
|
|
10 GRAPHIC 1,1
|
|
15 REM X=Radius
|
|
20 X=40:Y=0:TX=X:XO=160:YO=100
|
|
30 DRAW1,X+XO,Y+YO:DRAW1,Y+XO,X+YO
|
|
40 DRAW1,XO-X,YO+Y:DRAW1,XO-Y,YO+X
|
|
50 DRAW1,XO-X,YO-Y:DRAW1,XO-Y,YO-X
|
|
60 DRAW1,XO+X,YO=Y:DRAW1,XO+Y,YO-X
|
|
70 IF X<=Y THEN 100
|
|
80 Y=Y+1:TX=TX-Y
|
|
90 IF TX<0 THEN X=X-1:TX=TX+X
|
|
95 GOTO 30
|
|
100 END
|
|
|
|
===========================================================================
|
|
AFLI-specs v1.0
|
|
by written by D'Arc/Topaz for Chief/Padua on 28.6.1994
|
|
|
|
Advanged FLI is name I came up with during the time I coded the
|
|
first version of AFLI editor. I have never claimed to be the one
|
|
who discovered this new graphics mode for 64. I myself give the
|
|
credit for COLORFUL/ORIGO but I am not sure if anyone did it
|
|
before him (splits have been done but in my eyes they don't count).
|
|
|
|
In AFLI we can get 120 colors in theory (counted like this
|
|
16!/(2!*14!)=120). When we put red and blue hires pixels close to
|
|
each other we get a vision of purple - thanks the television.
|
|
|
|
AFLI is just like FLI with $08-$0f (hires value) in $d016 and a
|
|
couple of sprites over the first three marks. With $d018 we
|
|
change the start of screen memory. And the good old $d011 for the
|
|
main work.
|
|
|
|
AFLI is the same as FLI but we don't use the $d800-$dc00 area
|
|
for the third color. Actually we can't. In normal hires pictures
|
|
the colors on the picture is ordered in a normal screen (normal
|
|
text screen is on $0400+). The upper 4 bits is the color for
|
|
bit 0 in picture bitmap and the lower 4 bits is the color for bit
|
|
1 in picture bitmap (or the other way...but let us think that was
|
|
the right way).
|
|
|
|
For example: a normal hires picture char (8x8 bits)
|
|
|
|
01234567 in hires picture where 01234567
|
|
0 ***** the first spot of the 0bgggggbb
|
|
1*** *** screen has a value of 1gggbgggb
|
|
2*** *** $68 (blue&green) the 2gggbgggb
|
|
3******* hires picture looks 3gggggggb
|
|
4*** *** like this ----> 4gggbgggb
|
|
5*** *** b=blue, g=green 5gggbgggb
|
|
6*** *** 6gggbgggb
|
|
7 7bbbbbbbb
|
|
|
|
The bitmap is built just as in a hires picture bit 1 means the pixel
|
|
is on and 0 that the pixel is off.
|
|
|
|
In FLI we have built the screen to have badlines on every scanline of
|
|
the screen. This gives us the possibility to change the screenmemory
|
|
the picture uses on everyline. Now... when AFLI (and FLI) uses screen
|
|
memory for colors and we change the screenmemory start on everyline,
|
|
we can have new colors on everyline.
|
|
|
|
The screens are usually ordered like this.
|
|
|
|
screen memory used
|
|
0 $4000-$43ff
|
|
1 $4400-$47ff
|
|
2 $4800-$4bff
|
|
3 $4c00-$4fff
|
|
4 $5000-$53ff
|
|
5 $5400-$57ff
|
|
6 $5800-$5bff
|
|
7 $5c00-$5fff
|
|
$6000-$7fff BITMAP (the actual picture data)
|
|
|
|
The number of the screen is considered as the number of the line in
|
|
8x8 pixel area.
|
|
|
|
An example... Here we have cut from the memory showing the first
|
|
bytes in every screen.
|
|
|
|
screen/rownumber
|
|
00 01 02 03 04 05 06 07 08 09 10 11 12 13 ... 39
|
|
$4000 ff ff ff 56 .. .. ..
|
|
$4400 ff ff ff 67 .. ..
|
|
$4800 ff ff ff 91 ..
|
|
$4c00 ff ff ff b3
|
|
$5000 ff ff ff 54
|
|
$5400 ff ff ff 8f
|
|
$5800 ff ff ff 54
|
|
$5c00 ff ff ff 10
|
|
|
|
Actually the $ff won't have to be there. It will come to the screen
|
|
anyway. We have the same 'A' on the screen on the fourth mark ($6018-
|
|
$601f).
|
|
|
|
BITMAP AFLI PICTURE (number is the color number)
|
|
01234567 screenvalue 01234567
|
|
0 ***** $56 0 56666655 1=white, 0=black, 2=red ...
|
|
1*** *** $67 1 77767776
|
|
2*** *** $91 2 11191119
|
|
3******* $b3 3 33333333
|
|
4*** *** $54 4 44454445
|
|
5*** *** $8f 5 fff8fff8
|
|
6*** *** $54 6 44454445
|
|
7 $10 7 11111111
|
|
|
|
Now the 'A' surely has a lot of colors.
|
|
|
|
|
|
When we code a FLI routine we know that we have succeeded when we get
|
|
a 3 marks wide area filled with value $ff on the screen. In FLI the
|
|
thing is easily taken away; we just fill the three first bytes of a
|
|
line with empty bytes ($00). In AFLI the value $ff is a color. If we
|
|
try to clear the three first marks, we still have the gray area. WHY?
|
|
The $ff value comes to the screen.. so... the $ff is a color and
|
|
the upper four bits of the byte is the color for empty pixels. We can
|
|
not clear the first three marks to wipe the thing off. We have a new
|
|
lovely problem: we have to put black (or whatever) sprites over that
|
|
area. This is just timing.
|
|
|
|
|
|
This may look very complicated and I think you will still be asking
|
|
many questions from me - the text I have written surely ain't the
|
|
best novel ever written. I'm great in jumping from a thing to
|
|
another.
|
|
|
|
|
|
--
|
|
Chief/Padua +44 (0) 757 706791
|
|
--------------------
|
|
My opinions are not my employers
|
|
===========================================================================
|
|
Coding Tricks
|
|
|
|
The following are messages posted to comp.sys.cbm that contain little "coding
|
|
tricks" that I thought were useful. If you've got any of your own that you want
|
|
to post feel free to email them to me and I'll post them to comp.sys.cbm -
|
|
duck@pembvax1.pembroke.edu.
|
|
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
From: paulvl@python.es.ele.tue.nl (Paul van Loon)
|
|
Date: 11 Oct 1994 14:28:49 GMT
|
|
|
|
Hello everyone,
|
|
|
|
I really would like to start a thread on your favorite
|
|
sequences of 6502 code. I hope much people will react
|
|
so we can build up a library of the most beautiful
|
|
6502 code ever seen.
|
|
I would suggest little code fragments, probably not
|
|
longer than 10 lines, doing some tiny function.
|
|
|
|
I propose the following standard:
|
|
|
|
-----------------------
|
|
<xxx> <yyyyy> "text"
|
|
<aaa> ; comment
|
|
<bbb> ; comment
|
|
|
|
"description"
|
|
-----------------------
|
|
|
|
where <xxx> is the symbolic name (3 letters?) you give
|
|
to your code sequence, <yyyyy> describes your arguments
|
|
"text" gives a full name to pronounce for the symbolic name
|
|
and <aaa> and on are the 6502 instructions you use. "description"
|
|
is a description of the functionality of your code. The lines
|
|
with "---" are seperators.
|
|
|
|
I will hereby start with my all-time favourite code,
|
|
I discovered it only recently and I will also
|
|
show some examples of how to use it!
|
|
|
|
---------------------------------------
|
|
|
|
B7C "Bit 7 to Carry"
|
|
cmp #$80
|
|
|
|
This instruction copies bit 7 of A to C
|
|
---------------------------------------
|
|
|
|
This is really a beauty! It works because
|
|
cmp clears the carry if A is below the immediate
|
|
value, and sets it if A is higher or same.
|
|
All values lower than $80 have bit 7 equal 0,
|
|
cmp will clear C for all values below $80, and
|
|
thus will 'copy' bit 7 into carry. All values
|
|
equal or above $80 will have bit 7 equal 1,
|
|
cmp will set C for all values above $80 and
|
|
thus for this case it will also 'copy' bit 7
|
|
into carry!
|
|
|
|
------------------------------------------
|
|
ASR "Arithmetic Shift Right"
|
|
B7C
|
|
ror
|
|
|
|
This instruction does a signed divide by 2
|
|
------------------------------------------
|
|
|
|
Again a beauty in my eyes! I have puzzled
|
|
many times who to write the on other CPUs
|
|
well-known ASR instruction, but I never
|
|
seemed to get it implemented without an
|
|
extra register to use like:
|
|
tax
|
|
asl
|
|
txa
|
|
ror
|
|
or even without a branch (figure that out
|
|
yourself!).
|
|
|
|
With these two beauties I want to give you
|
|
an idea of what I mean, and please follow me
|
|
up!.
|
|
|
|
BONUS. A little routine to clear the screen
|
|
without erasing the sprite pointers.
|
|
|
|
-----------------------------
|
|
CLS "Clear the screen"
|
|
ldx #250
|
|
lda #$20
|
|
clp sta $0400-1,x ; 0-249
|
|
sta $0400+249,x ;250-499
|
|
sta $0400+499,x ;500-749
|
|
sta $0400+749,x ;750-999
|
|
dex
|
|
bne clp
|
|
|
|
Clear only the screen
|
|
----------------------------
|
|
|
|
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
From: paulvl@python.es.ele.tue.nl (Paul van Loon)
|
|
Subject: Re: Post your favourite little code too!
|
|
Date: 20 Oct 1994 12:17:40 GMT
|
|
|
|
-------------------------------------
|
|
RSL "Rotate Straight Left"
|
|
B7C
|
|
rol
|
|
|
|
This instruction does a rotation left
|
|
through 8 bits (rol does a rotation
|
|
left through 9 bits, 8 bits of A
|
|
and 1 bit C)
|
|
-------------------------------------
|
|
|
|
Another useful instruction, for
|
|
a wraparound rol, e.g. for rotating
|
|
bytes in characters to simulate
|
|
parallax scrolling.
|
|
|
|
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
|
|
A reliable way to create a Straight IRQ?
|
|
|
|
Well, this is the way I've always used, because it's reliable, flexible
|
|
doesn't mess up, or set restictions on the display, unlike routines that
|
|
depend on D011 etc...
|
|
This one only observes the VIC chip:
|
|
|
|
It's some time since I actually coded a routine of this kind,
|
|
and I'm writing this off memory, so there might be some errors in the
|
|
following code.
|
|
|
|
<irq is initiated in the usual way, I almost always use FFFE/FFFF instead
|
|
of 0314/0315 for interrupt vectors, but the routine should work with
|
|
0314/315, but delay timing will be different becouse of the time being
|
|
up by the kernal>
|
|
|
|
<Set up irq vector to label 'irq1' and program desired rasterline with
|
|
d012 & $ d011 in the usual way>
|
|
|
|
<other initiation of program>
|
|
...
|
|
|
|
jmp mainPrg
|
|
|
|
;----------- Standard routines for all sharp irqs ----------------
|
|
|
|
sharp inc $d012 ;Set interrupt to the next line.
|
|
inc $d019 ;Ready for new interrupt
|
|
;same as lda #1 : sta $d019
|
|
sta storeA
|
|
lda #<sharpirq
|
|
sta $fffe ;Alter interrupt vector
|
|
lda #>sharpirq
|
|
sta $ffff
|
|
cli ;Allow new interrupt even if we
|
|
;still are in the previos irq call
|
|
noploop
|
|
nop
|
|
nop ; We add nop's here so that the next interrupt
|
|
; will interrupt while we are executing nop
|
|
... ; commands. Since nop commands use two cycles
|
|
... ; the interrupt will at most be delayed 1
|
|
... ; cycle.
|
|
... ; I think we needed about 11 nop's to be sure
|
|
; that execution is interrupted while they are
|
|
; run. (I'm not sure about this number)
|
|
|
|
jmp noploop ; Although we are sure that the interrupt will
|
|
; interrupt before we reach this point, we
|
|
; add this loop to be on the safe side.
|
|
; Imagine if the program happened to be frosen
|
|
; by a carterigde and later restarted while
|
|
; executing the nop's.
|
|
|
|
|
|
sharpirq pla ; Delete data put to stack by the last
|
|
pla ; interrupt, we don't intend to return from
|
|
pla ; it.
|
|
stx storeX
|
|
sty storeY
|
|
|
|
nop ; Now we only have an uncertainty of 1 cycle
|
|
nop ; as to where the interrupt is.
|
|
; By waiting until the edge of the current
|
|
; rasterline we can determine if the interrupt
|
|
... ; was delayed by one cycle or not.
|
|
... ; (How many nop's that is required to reach the
|
|
... ; edge of the rasterline i don't remember.
|
|
; You'll just have to find it out yourself)
|
|
; These nop's may of course be exchanged by
|
|
; equivelent time consuming instructions.
|
|
|
|
lda $d012 ; get current rasterline
|
|
cmp $d012 ; still on same line = was delayed;
|
|
bne addCycle ; add 1 cycle if not delayed.
|
|
addCycle ; doing a branch jump takes 1 cycle more than
|
|
; not doing one.
|
|
|
|
; the rastertiming is now 'straight'/sharp
|
|
rts ; return to routine that cal led sharp
|
|
|
|
|
|
endIrq ; restore a x y
|
|
storeA = * + 1
|
|
lda #0 ; For those who don't like self modifying code
|
|
storeX = * + 1 ; this can be changed easy.
|
|
ldx #0
|
|
storeY = * + 1
|
|
ldy #0
|
|
rti ; Return from interrupt.
|
|
|
|
nextIrq
|
|
stx $fffe
|
|
sty $ffff
|
|
sta $d012
|
|
inc $d019 ;or lda #1 : sta $d019 if you prefere
|
|
rts
|
|
|
|
;------------------ The actual interrupt ------------------------------
|
|
|
|
irq1
|
|
jsr sharp ;Make interrupt sharp and store A X Y regs.
|
|
|
|
<here you put your sharp interrupt dependant code>
|
|
|
|
<other code that you need executed this interrupt>
|
|
|
|
ldx #<irq<?> ;<?> is the next interrupt that is due
|
|
ldy #>irq<?> ;1 if irq1 is the only interrupt.
|
|
lda #<rasterline for interrupt>
|
|
jsr nextIrq
|
|
|
|
jmp endIrq
|
|
|
|
;--------------- Main program ---------------------------------
|
|
|
|
mainPrg
|
|
cli ;allow interrupt
|
|
|
|
<wait for space or something>
|
|
|
|
sei
|
|
|
|
<shut everything down and restore what needs to be restored>
|
|
|
|
rts ; Or jmp exitCode
|
|
|
|
*******************************************************************
|
|
|
|
By adding this routine:
|
|
|
|
saver sta storeA
|
|
stx storeX
|
|
sty storeY
|
|
rts
|
|
|
|
...you can at any time turn on/off the sharping by exchanging 'jsr sharp'
|
|
and 'jsr saver'.
|
|
|
|
I hope this is what you were looking for. Sorry for not supplying a
|
|
complete source, but as I said this is all from memory.
|
|
|
|
PS. My native lanuage is not english and I typed this in a hurry, so sorry
|
|
for the lousy lanuage / source.
|
|
|
|
Furhermore, ways of getting an rnd.
|
|
|
|
* lda $d012, only in large complex programs with lots of variable execution
|
|
times where rnd is only needed once in a while.
|
|
(Never use in raster interrupts of obvious reasons)
|
|
|
|
* lda $d800 ; The 4 upper bits of mem at area $d800 - $dc00 are completly
|
|
lsr ; unpredictable...
|
|
lda $d801
|
|
lsr
|
|
lda $d802
|
|
...
|
|
|
|
* Read the values from the white noise generator to the SID.
|
|
3rd voice set to whitenoise, and read the result.
|
|
(sorry don't remember address to read, think it is around $d416-$d41c)
|
|
|
|
These give you _true_ random numbers. It's also possible to create
|
|
'random' routines that create 'random' numbers based on a seed value.
|
|
|
|
Anything unclear? Mail me at s514@ii.uib.no
|
|
|
|
Bye...
|
|
|
|
- Rolf Wilhelm Rasmussen
|
|
|
|
Equal of Eternity
|
|
|
|
===========================================================================
|
|
C.S. Bruce Interview
|
|
by Craig Taylor (duck@pembvax1.pembroke.edu).
|
|
|
|
The following is an interview of Craig Bruce, author of numerous programs such
|
|
as ZED, ACE etc for the Commodore 64/128.
|
|
|
|
> What computer experience did you have before the Commodore computers?
|
|
|
|
Very little. I was 14 at the time, in grade nine, and it was December 1982.
|
|
My Jr. High school had a few CBM 8032s, but I never actually got to touch
|
|
one. I took a "mini course" on computers and learned a tiny little bit about
|
|
what computers were like and about BASIC. To give you an idea of how little
|
|
I learned, I was entirely incapable at the time of figuring out how to
|
|
increment a variable (X=X+1).
|
|
|
|
> What was your first Commodore computer and why?
|
|
|
|
My first computer was a VIC-20. I had the choice narrowed down to a VIC, a
|
|
TI99-4A, or a Timex/Sinclair 1000(?). (I don't think I had heard of the
|
|
Ataris or the Apple). I chose the VIC partly because it was related to the
|
|
computers at school but mostly because it had the most impressive brochure
|
|
and I had the most information about it. It was theoretically a Christmas
|
|
present, but it didn't stay in the box very long. I paid half of the $400.00
|
|
price tag and my parents paid the other half.
|
|
|
|
> How did you learn programming on the Commodore? Did experience from other
|
|
PC's help?
|
|
|
|
Ahhh, those were the days. I learned programming from a few sources. The
|
|
user's guide that came with the VIC was quite helpful, and I read the
|
|
magazines of the day, mostly Compute!s. I also had a friend who went to high
|
|
school and used the computers there who knew a thing or two, and two other
|
|
friends who got VIC-20s soon after me, so we learned from each other.
|
|
|
|
I also took a relatively informal night course that was offered for
|
|
programming VIC-20s. By the time I took it, though, I had already learned
|
|
just about everything that the course tought: BASIC. Then, in the last
|
|
class, the instuctor talked just a little bit about machine langauge, just
|
|
enough for me to understand what was in the VIC-20 Programmer's Reference
|
|
Guide. I learned 6502 machine language shortly after that.
|
|
|
|
Experience from other PCs didn't really factor into things, since I had no
|
|
experience with any other PC. However, when the time came to learn about
|
|
other computers and other programming languages (in high school and bachelor
|
|
university), I had an enormous advantage over the other students, since I
|
|
understood so thoroughly how computers worked because of my VIC-20
|
|
experience.
|
|
|
|
> What other interests, besides hacking on the Commodore, do you have?
|
|
|
|
Not many. I don't get out much and I have only a handful of friends. I
|
|
spend most of my time sleeping, watching TV, net surfing, doing school work,
|
|
and/or hacking on Commodores. Hacking on Commodores is in my blood. While
|
|
I'm doing these other things, I'm usually thinking about hacking on
|
|
Commodores. You might say that I'm a sterotypical total computer geek. I do
|
|
like biking, though. I bike to school every day. And music.
|
|
|
|
> What is your feelings on the demise of Commodore?
|
|
|
|
Losing Commodore was a little sad, but as someone said on the newsgroup when
|
|
Commodore went under, "What has Commodore done for you lately?". We
|
|
8-bitters lost all support from Commodore long before its demise. I
|
|
certainly don't blame them; 8-bit computers are a thing of the past and there
|
|
wasn't a big enough market to support a company the size of Commodore.
|
|
|
|
> Do you see anything in the future that signals anything that will extend the
|
|
useful lifetime of the Commodore 8-bits?
|
|
|
|
IMHO, there are only two things that 8-bit computers need to survive in the
|
|
hands of hobbiests indefinitely: serious system software and modern hardware
|
|
peripherals. Some serious system software has begun to surface recently :-),
|
|
and Creative Micro Designs has been providing modern peripherals for us to
|
|
use. New application programs are needed too, but I'm assuming that
|
|
hobbiests + serious_system_software --> serious_new_application_programs.
|
|
(Perhaps this is a bit self-serving).
|
|
|
|
Eight-bit computers have two big advantages over bigger PCs: a much lower
|
|
price and a much higher understandability quotient. Both of these are very
|
|
important to hobbiests.
|
|
|
|
> Do you feel that with the addition of newer periphials that are gradually
|
|
superceeding the CPU's job (REU, RamLink, SwiftLink etc...) that the
|
|
Commodore 8-bit standard machine is no longer standard??
|
|
|
|
Indeed, there are lots of options. But I think that this is a good thing.
|
|
The original Commodores are quite limited, and this modern hardware is needed
|
|
to allow the Commodores to remain useful in the networked world. An
|
|
important feature of all of these new products is that you can flip a switch
|
|
or pull out a cartidge and you're back to your little old standard Commodore.
|
|
Of course, who really wants to do this.
|
|
|
|
The reason that I have stayed with my little Commodore for all of these years
|
|
is that I believe that it still has quite enough power (using modern
|
|
peripherals and expanded memory) to do what I require of it. For example,
|
|
it's quite possible to have a nice little 17K text editor that can edit huge
|
|
files and be very useful. You don't need a multi-megabyte program with all
|
|
kinds of snazzy features that requires a monsterous machine to run on to do
|
|
this. These multi-megabyte programs are simply bloated and poorly designed.
|
|
|
|
> Will there ever be an update to Zed? (a question asked on a lot of the
|
|
commericial providers)
|
|
|
|
Yes. I've been promising this for a long time, but the right time to do this
|
|
is finally near.
|
|
|
|
> What is the process that you use for writing your programs?
|
|
|
|
I'll start this answer with a Unix-fortune quotation:
|
|
|
|
"Real programmers don't draw flowcharts. Flowcharts are, after all, the
|
|
illiterate's form of documentation. Cavemen drew flowcharts; look how
|
|
much good it did them."
|
|
|
|
For complicated algorithms, I'll sit down write some pseudo-code, and I
|
|
always plan and write out complicated data structures. But other than this,
|
|
I usually just sit down and write code, after kicking ideas around in my head
|
|
long enough for me to know what I have to do.
|
|
|
|
> It's been noticed that you have a "fanatacism" about speed in your programs.
|
|
Can you elaborate on this?
|
|
|
|
Guilty as charged. As I said above, I despise bloated software that needs a
|
|
mega-machine to run on fast enough. I like software that is sleek and mean,
|
|
and I have an axe to grind that little 8-bit Commodore computers offer quite
|
|
enough computing power for most applications that most people would use them
|
|
for. The exceptions are number-crunching, huge-data processing, and heavy
|
|
computation. However, for most interactive programs, an 8-bit processor is
|
|
quite enough. So, I grind my axe by producing fast programs. Arguably, that
|
|
effort is sometimes misspent (like in the printing to the screen in ACE -- I
|
|
still have a few more tricks up my sleeve though...), but I like to go a
|
|
little too far sometimes to make people go, "Wow! I didn't know this little
|
|
machine could do that so fast!!" I like to upset the notion that you need a
|
|
huge machine to get adequate performance. (In fact, sometimes the opposite
|
|
is true, since programmers assume that they can be extra sloppy when
|
|
programming for huge machines).
|
|
|
|
As a user, I like crisp responsiveness. This is a feature of personal
|
|
computers that can sometimes be absent on big multi-user virtual-memory
|
|
machines.
|
|
|
|
I also have a big thing against backwards compatibility ("hysterical
|
|
raisins"). This is a significant cause of software bloatedness. This is one
|
|
reason that ACE, for example, was designed from scratch rather then with the
|
|
pre-set limitation that all BASIC-compatible programs should run with it (a
|
|
la CS-DOS).
|
|
|
|
> Is there anything that you find particularly useful / handy about the
|
|
Commodore's architecture?
|
|
|
|
Yeah, it's simple.
|
|
|
|
> And the corallary: Is there anything particularly annoying?
|
|
|
|
Yeah, it's limited.
|
|
|
|
> What is currently in the works / planned??
|
|
|
|
My Commodore job queue looks like the following:
|
|
|
|
1. Update my Unix VBM file filter. Make it produce a new format of VBM files
|
|
with run-length encoding compression. Investigate LZW(?) compression.
|
|
|
|
2. Work on ACE release #13: Internal cleanup. Reorganize the internal memory
|
|
usage, add features to the command shell, clean up memory and device
|
|
management inside the kernel, update the VBM program, add a SwiftLink
|
|
device driver, make a simple glass-tty terminal program.
|
|
|
|
3. Develop a new portable archiver format and write a C program for Unix,
|
|
".car" format.
|
|
|
|
4. Write my next article for C= Hacking, which will be about the detailed
|
|
design of a distributed multitasking microkernel operating system for
|
|
the C128. This article will also include a minimal multitasking
|
|
implementation.
|
|
|
|
5. Work on ACE release #14: Port Zed to ACE. Get the basic editing features
|
|
going.
|
|
|
|
6. Work on ACE release #15: Finish the ACE assembler. Add the file-inclusion,
|
|
conditional and macro assembly features. Make it accept more dyadic
|
|
operators in expressions, fix the label typing, make it generate
|
|
relocatable executable code modules, and make it handle modular compilation
|
|
(".o" files).
|
|
|
|
7. Work on ACE release #16: Archiving. Update the "bcode" and "unbcode"
|
|
programs to support uucode, nucode and hexcode formats. Toss the old
|
|
"uuencode" and "uudecode" programs. Implement "car" and "uncar" programs.
|
|
Look into "zip" format.
|
|
|
|
8. Start on BOS, the distributed multitasking microkernel operating system
|
|
for the 128.
|
|
|
|
(Actually, some of these things will be done by the time that C= Hacking
|
|
comes out).
|
|
|
|
Keep on Hackin'!
|
|
|
|
===========================================================================
|
|
Aligning 1541 Drives
|
|
by Ward Shrake (taken from comp.sys.cbm)
|
|
|
|
A discussion regarding Commodore 1541 disk drive alignment procedures, with
|
|
suggestions.
|
|
|
|
Background information.
|
|
|
|
The best way I've ever seen to consistently and reliably get a 1541 disk
|
|
drive aligned perfectly, was caused by copy protection. It is sort of appropo
|
|
that copy protection, which usually causes the "head knock" problem that puts
|
|
drives out of alignment in the first place, should also be able to solve the
|
|
problem it created.
|
|
|
|
An older version of a disk utility program, ("Disector" v3.0, as I remember
|
|
it), had copy protection that would not let you load the disk up unless your
|
|
disk alignment was perfect. While initially loading itself, it would search
|
|
and search, never quitting, until it found what it was looking for, exactly
|
|
where it was looking for it. It would stay in an endless loop, searching
|
|
forever, never making it to so far as the first screen. This essentially
|
|
"locked up" the computer, if the program thought the disk it was on was an
|
|
illegal copy.
|
|
|
|
This quickly became the most hassle-free, no-worry alignment program I've
|
|
ever seen. I have seen and used most of the others; this method beat them
|
|
all, no contest, in my opinion.
|
|
|
|
The other programs, the ones made for aligning your drive, never
|
|
consistently worked acceptably well, in my experience. Other technical users
|
|
apparently feel the same way about them, as the "General FAQ, v2.1" on
|
|
Commodores points out. They would work OK part of the time, or on part of the
|
|
drives you tried, but not all, I found. Or they would say you now had a
|
|
perfectly-aligned drive, but some difficult copy protection schemes would
|
|
still not load and run on the newly tuned-up drive friend. A friend of mine,
|
|
now deceased, once had a drive no alignment program could fix. We tried
|
|
everything we could find. After aligning it with a given method or program,
|
|
some programs would load that would not load before, but others would now no
|
|
longer work, that used to work before. All in all, it was very frustrating,
|
|
and the general feeling was that there has to be a better, easier, more
|
|
reliable way to do this.
|
|
|
|
All an alignment program has to do, is to make sure that when the disk drive
|
|
says it is precisely at a given track's physical location, that it is really
|
|
there, centered on that track.
|
|
|
|
There are other Commodore adjustments, but alignment seems to be, by far, the
|
|
most common problem. Disk drive rotational speed can be adjusted, but it
|
|
usually is not the problem. In fact, I've seen more than one drive, that when
|
|
adjusted to read a program-reported "perfect" 300 rpm rotation speed, they
|
|
quit reading disks; requiring speed to be set at a reported 310 rpm, to work
|
|
again. The end stop gap can also be adjusted, but I've never seen it be the
|
|
real culprit with a non-working disk drive. Your experience may vary, of
|
|
course, but I've always found that it is best to concentrate on alignment
|
|
first, then fool around with the other adjustments ONLY after alignment is
|
|
truly corrected, and only if it still refuses to work properly.
|
|
|
|
Once alignment is corrected, there are methods available to insure that it
|
|
stays that way. For instance, you can have the stepper motor's pulley
|
|
mechanically pinned to its shaft, instead of merely relying on the factory's
|
|
interference fit to hold it. Commodore 1541 drives were made to be self-
|
|
aligning, apparently, which would be fine if "head knocking" protection
|
|
schemes were not around. Since they are, the pulley should, ideally, not be
|
|
allowed to turn on its shaft, which is what causes misalignment problems.
|
|
|
|
How I used to align 1541 disk drives....
|
|
|
|
To precisely align a given 1541 disk drive, I used the old, unbroken copy I
|
|
had of Disector (v3.0, I think), and followed these steps. With power to the
|
|
drive off an disconnected, you first took off the upper and lower halves of
|
|
the outer plastic casing of the drive. This exposed the electronics inside.
|
|
You then found and loosened (but not removed!) the two stepper motor mounting
|
|
screws, which are on the underside of the disk drive's internal mechanisms.
|
|
After that, you hooked the power cable back up, and hooked the drive to the
|
|
computer like it normally is.
|
|
|
|
Once you've done this, you set the drive up on one side, so that you can
|
|
(carefully!) reach into the mechanism, to physically rotate the stepper
|
|
motor, which would normally be on the bottom of the drive. You type in the
|
|
program's loading instructions on the computer, and you then wait until the
|
|
screen went black (copy protection searching for certain info on the
|
|
diskette). This is where the program "locks up," with the unaligned drive.
|
|
|
|
Once the program is loading, but stuck and unable to find what it wants, you
|
|
reach into the mechanism, very slowly and carefully, turning the stepper
|
|
motor a slight bit in either direction, and stopping. Tiny adjustments are a
|
|
lot; don't overdo it. Be patient; don't go too fast, or move it too much! You
|
|
watch the screen carefully, and listen to the drive's sounds.
|
|
|
|
When you have rotated the stepper motor to the proper place, the sounds and
|
|
the screen will act a little different, perhaps only slightly so. Wait a
|
|
second, not moving the stepper motor at all. When you are right on,
|
|
alignment-wise, the program will find what it is looking for, and the
|
|
program's main menu will appear.
|
|
|
|
Once the main menu has come onto the screen, you have a perfectly aligned
|
|
drive. Then you have to retighten the stepper mounting screws, being very
|
|
careful not to accidentally move the motor in the process. Hold the motor
|
|
firmly while retightening both screws in small steps, alternating back and
|
|
forth between them until they are both tight. The rotational force of the
|
|
screws turning, forces the motor to move some, so watch for it.
|
|
|
|
With this method, using a specially-prepared disk, I always got perfect
|
|
results; everything would load, every time, from then on. (Assuming that the
|
|
disk was formatted with a good drive to begin with; any disks you made
|
|
recently, on your badly-aligned drive, may not load after the alignment
|
|
procedure. Transfer the info on these disks, to a second, known-good drive,
|
|
before you do this procedure. This is normal, however, no matter what method
|
|
you use to align a bad drive.)
|
|
|
|
Here's the problem with this method...
|
|
|
|
This procedure only works with a special disk, one that is no longer
|
|
available. With the special disk, alignment is quick, hassle-free, and it
|
|
always gave excellent, reliable results the first time around. Without the
|
|
"perfect" disk, this procedure is worthless. This is obviously a problem,
|
|
since the method relies on a disk that is no longer available to the public.
|
|
You can't make your own, because you don't know if the disk drives you are
|
|
using, are truly perfect to start with! Disks made by users, on Commodore
|
|
equipment, never worked; they just matched your drive's alignment to that of
|
|
someone else's equipment, which may be borderline bad to start with.
|
|
|
|
Here's what I suggest to solve this...
|
|
|
|
Your mission, should some hot programmer out there choose to accept it, is to
|
|
create a program that will create a "special" disk, and a
|
|
Commodore-compatible program to try to read that special disk.
|
|
|
|
Ideally, the Commodore-compatible reading program would be short and simple
|
|
enough to fit inside 8k of memory, so it could fit on a cartridge. This would
|
|
allow it to work, even if a user's disk drive would not load programs
|
|
anymore. It could still be stored on a diskette, too, with a little planning.
|
|
|
|
Theoretically, once you had the specially-formatted diskette, and the program
|
|
on cartridge, you would only need a screwdriver to take the drive apart, and
|
|
a Commodore microcomputer to run the program on. No other special tools would
|
|
be needed, and very little technical knowledge would be required; just some
|
|
general safety tips, because you are working around sensitive electronic
|
|
parts, with wall current coming into the drive itself, at least on older
|
|
1541's.
|
|
|
|
Why should a programmer go to all the trouble?
|
|
|
|
I'm sure there are a lot of people out there who could use this, if some hot
|
|
programmer should decide to write it, and make it available to the rest of
|
|
us. There are always programmers out there, somewhere, eager to show off
|
|
their computer skills, and their creativity. It is one of the things that
|
|
makes the computer community so great in the first place. (If people out
|
|
there can write IBM-to-Commodore disk file readers, this should be a breeze!)
|
|
|
|
Techies should appreciate it as a great, reliable and cheap way to align
|
|
troublesome disk drives, and those people with a C64 in a closet would sure
|
|
appreciate their technical buddies getting their dead systems going again!
|
|
|
|
Description of what I have in mind, as to how it should work.
|
|
|
|
You need a Commodore computer (the 64 is most popular), one 1541 disk drive
|
|
that needs alignment (or that you want to check), a screwdriver to open the
|
|
drive up, a specially-prepared disk used only for alignment purposes, and a
|
|
computer program that would run on the Commodore that would look at and
|
|
analyze the information that is on the specially-prepared diskette, as the
|
|
computer program tries to read it.
|
|
|
|
OK. Here's where it gets cute.
|
|
|
|
The problem with most disk alignment methods, as I see it, is that it relies
|
|
totally on technology that the Commodore has available; trying to create a
|
|
special disk on a 1541, I just don't see as being realistic, or the best way
|
|
to do it. The 1541 has many limitations, compared to some other disk drives
|
|
which operate on other computer platforms. Don't get me wrong; I love
|
|
Commodore computers, and have for years. But, realistically, the 5.25 inch
|
|
drives found in say an IBM machine, are just plain better in many ways than
|
|
the 1541. They are made to hold much more information, and to do that, they
|
|
have to be much more precise in doing so than the 1541 was ever designed to
|
|
be.
|
|
|
|
If a person were to do this, I would suggest that they write an IBM program
|
|
that would use a high density, 1.2 megabyte capacity, 5.25-inch type of disk
|
|
drive to create the special diskettes, which the 1541 would later read.
|
|
|
|
Doing this would allow the creation of very thin tracks on the diskette's
|
|
surface, spaced closely together. This would, within the limitations of the
|
|
1541's read head, allow the Commodore to "see" precisely where it currently
|
|
was, to one side or the other of some "centered" position. The advantage of
|
|
thin tracks, widthwise, is that the read head won't see them at all,
|
|
reliably, unless you are exactly, perfectly right on top of them. Another
|
|
advantage to this, again within the limitations of the 1541's read head
|
|
(whatever that may be), is that left or right of center, the head would
|
|
likely pick up the next track over, letting you know you were off by a
|
|
certain amount automatically.
|
|
|
|
I hope I'm making myself clear, in my explanation of this. If I am not, Email
|
|
me with your questions, and I'll try to answer them better, and/or update
|
|
this file, to entice someone else to work on this. I really would like to see
|
|
it done. (Current Email address, as of Sep 94: wardshrake@aol.com on the
|
|
Internet, or just WardShrake on AOL. Will soon have a Compuserve Email
|
|
address, too: I'll be user 75207,1005 there, or 75207.1005@compuserve.com on
|
|
the Internet.)
|
|
|
|
Anyway, let's continue. With the IBM creating a specially-made disk just for
|
|
this one purpose, you would not even have to worry about following any
|
|
standard formatting procedures. No user-stored data would ever be written to
|
|
the diskette, so standard sectoring could be safely ignored. You could create
|
|
any signal or sectoring scheme you like, as long as the IBM could create it,
|
|
and the Commodore could read it; and you'd be writing both programs, anyway,
|
|
making this easier to insure, right?
|
|
|
|
I can hear some die-hard Commodore users saying, "I hate IBM's" or "I don't
|
|
even have an IBM" or some such. Fine. Not a problem. If all the
|
|
IBM-compatible program did was to create a special floppy disk, once, then
|
|
quit, you would not even need to OWN an IBM, you'd just need to be able to
|
|
USE one for a few minutes.
|
|
|
|
Even if you don't have access to one at work, and don't know of anyone who
|
|
has one to lend you, I will stick with this suggestion, because I know that
|
|
some businesses that make photocopies often also rent IBM's and Mac's on an
|
|
hourly basis, for very little money. My local Kinko's copy center rents them
|
|
both at $10.00 an hour. You would only need it for a few minutes or so.
|
|
|
|
The diskette-creation program would only need a few minutes to run, to make
|
|
up a special disk, so you'd only be paying for a good quality, blank high
|
|
density floppy, and ten or fifteen minutes of rental time, tops. The copy
|
|
center person may even be able to start up the floppy-based IBM program for
|
|
you, if you don't know how to do it yourself. That should come to $5.00 or
|
|
less, even if you don't own or normally have access to an IBM compatible
|
|
computer! You can't beat that, for a utility to align equipment!
|
|
|
|
OK. In overview, you'd need to use an IBM-compatible computer, just long
|
|
enough to load an IBM-compatible program which would create one special,
|
|
5.25" diskette, perhaps on a high density floppy. You would then open up your
|
|
Commodore drive's case, and start up a special program on your Commodore 64,
|
|
to read the created diskette. (Again, an 8k Commodore program would fit very
|
|
easily on a cartridge, for easiest loading and running.)
|
|
|
|
While the computer and drive were running, you would (very carefully, and
|
|
observing safety precautions) loosen the stepper motor's screws, and slowly
|
|
turn the motor clockwise and counter-clockwise, until the Commodore program's
|
|
screen info told you that you were exactly where you should be, right over
|
|
the proper track. Not to the left or right of it, but in perfect alignment.
|
|
|
|
Because the Commodore disk-reading program would be "on" constantly, and
|
|
reporting any small changes to you via information on your screen, you would
|
|
only have to take a few minutes of fiddling, doing a simple, non-technical
|
|
turning of the stepper motor, to get the drive aligned. The two computer
|
|
programs that would make up this package would be doing most of the work.
|
|
|
|
I imagine a drive could be perfectly aligned, and back in running order, in
|
|
fifteen minutes or less. Five, if you paid attention to the process, and had
|
|
some practice before. Remember, this is based on an alignment procedure I
|
|
really used to do, using a heavily-protected diskette, so I am extrapolating
|
|
from my personal experiences, even though I'm talking about a theory here.
|
|
|
|
I don't see where there would be any easier, simpler method of doing a disk
|
|
alignment. The user wouldn't even have to know a thing about tracks and
|
|
sectors; they would just loosen two screws, following some instructions, and
|
|
turn the motor. What could be any easier?
|
|
|
|
The program could, if it was really creative and well-done, tell them to
|
|
rotate the motor clockwise or counter-clockwise (as they face it), to dial
|
|
the motor precisely in. Tracks to either side of an arbitrary (track 18?)
|
|
center position would say to go one way, tracks on the other would say the
|
|
reverse. When you turn it too far one way, it would reverse its instructions
|
|
to you; you would know you were very close then. When you were "right on,"
|
|
the program would tell you so. You'd lock the screws down, carefully, and as
|
|
long as you hadn't jiggled the motor when you tightened it back down, you
|
|
would be all done!
|
|
|
|
How much easier could it be, right? (On the final user, that is!)
|
|
|
|
If anyone is interested in doing this, or goes out and does it, please let me
|
|
know via Email. I'd like to hear about it. Again, it would be something
|
|
possible, useful, and a really neat trick. I know there are people out there
|
|
that program on both the IBM and the Commodore; the various cross-reading
|
|
programs attest to that, well enough!
|
|
|
|
Ward Shrake
|
|
Covina, California
|
|
|
|
==================================================================---END---===
|