Return to the archive home page |
Gazette Feedback October 1985Editors and Readers Do you have a question or a problem? Have you discovered something that could help other Commodore users? Do you have a comment about something you've read in COMPUTEI's GAZETTE? We want to hear from you. Write to Gazette Feedback, COMPUTEI's GAZETTE, P.O. Box 5406, Greensboro, NC 27403. We regret that due to the volume of mail received, we cannot respond individually to programming questions.
Safety SavesThe "MLX" program allows you to type part of a machine language program, save it to disk, and return later to complete it by listing the new line. Are there any commands available to do this with BASIC programs? Ancel W. Norris It's a good idea to periodically save a copy of a program you're working on, whether it's in BASIC or machine language. Computer memory is active only as long as the power is on. If the power were to fail, even for a brief moment, you would lose everything you had typed since the last save. Some people make a safety save every ten minutes, while others may wait half an hour. The "MLX" program does two things when you type SHIFT-S to save. First, it takes the program name you've chosen and tries to scratch a program by that name from the disk (if there's no program under that name, nothing happens). Then it saves the new copy of the program. The reason for scratching first is that the disk drive does not allow you to save a program if there's another program with the same name already on the disk. If a disk contained two programs named "adventure" and you tried to LOAD"0: ADVENTURE",8 the disk drive wouldn't know which program you wanted. So if you're accustomed to using the same name for partially typed MLX programs, you'll have problems saving unfinished BASIC programs. You could scratch the earlier version before saving, or just number the different versions of a program: ADVI, ADV2, ADV3, and so on. When you've finished, use the question mark wild card to scratch all of the earlier versions (OPEN 15,8,15, "SO:ADV?" will scratch all programs with a four letter name beginning with the letters "ADV"). But make sure you don't scratch the final version-give it another name or save a copy to another disk.
Joystick Rapid FireSeveral months ago, "Gazette Feedback" said POKE 650,128 makes keys repeat when they're held down. Is there a POKE to give rapid fire for the joystick? Brian Patz The fire button is an electrical switch. As long as you hold down the button, the circuit is live. So, in a sense, the joystick button already repeats. So why do some games make you press the button again and again to get rapid fire? Many such programs look first for the button to be pressed and then take the appropriate action (a character jumps, a laser is fired, or whatever). The program then waits for the button to be released., You would have to rewrite parts of the software to allow a rapid fire effect. It might also be possible to alter the wiring of the joystick to cause the button to send pulses rather than a steady current. A modification like this would cause the button to repeat. But there isn't a single POKE you can enter, you have to change either the software or the joystick.
Unusual LinesI have two questions about "Dynamic SID Editor" in the June issue. How is it possible to use just one quote mark in a PRINT statement (line 50310)? And what is the purpose of starting a line with a colon (line 50330)? Dennis R. Waldron Quotation marks signal the beginning and end of a string to be printed, PRINT "LIKE THIS" for example. The second quotation mark is required if you want to add a colon and another command to the line. But if it's the last item on a line, the second quote is unnecessary. The computer assumes that the end of a line is also the end of a print statement. The technique of leaving off the final quotation mark is often found in programs for the unexpanded VIC because it saves one byte of memory. Colons, like quotes, are also delimiters; they separate commands on a line. BASIC automatically throws away extra spaces between a line number and the first command on the line. But if a line begins with a colon, you can add as many spaces as you want. Some programmers like to indent FOR-NEXT loops using colons followed by spaces because it makes listings easier to read. You can also put a single colon on an otherwise blank line or two, to separate different sections of a program.
Starting A User GroupThere are 20-30 Commodore owners in my area, but no user groups in sight. I was wondering if you could give me some tips on starting one. Mike Scott All you need to start a user group is several interested people, someone who is willing to do the initial organizing, and a place to meet. It sounds like you already have the first two requirements. Set a time and place for your first meeting. It could be at someone's home (if you think the group will be small enough), a school classroom, the back room of a local computer store, or anyplace else that is available. Advertise the meeting by posting notices in public places-stores, laundromats, restaurants, schools-and try to get the notice printed in your local newspaper or announced on a radio station. Also, some cable TV systems have a public notice channel for various announcements. Your first meeting will probably be spent determining the group's interests and goals. Officers will need to be appointed or elected. They'll be responsible for planning and leading future meetings, and taking care of other administrative tasks. You could have everyone fill out a sheet indicating what kind of equipment they own, what they want from the group, and also what they feel they could contribute. Suggestions for content of future meetings is also important. Each meeting should allow time for two. things: information for the group members (software reviews, news from Commodore, demonstrations of peripherals, tutorials, speakers on topics of interest, etc.), and input and questions from the group members. Stay in close touch with the group, and modify the structure when necessary (breaking off into sub groups for special interests, or starting a bulletin board system to facilitate better communication, for example). Some user groups arrange for discount rates on blank disks or group purchases at a lower price than software from local computer stores. In some states or cities you may have to obtain a tax permit or vendor's license to legally, sell items like this. Most groups also maintain a library of public domain software programs written by members and donated to the user group for free distribution to other members. You may want to get in touch with other user groups to see how they got started. Commodore provides support to user groups-contact Pete Baczor, Commodore Business Machines, 1200 Wilson Drive, West Chester, PA 19380, (215) 431-9264.
Another Way To Quash Question MarksIn the March issue someone asked about how to get rid of the question mark in an INPUT statement. Your suggestion of OPENing a file to the keyboard is one way to do this. Here's another:
10 POKE19,1 Location 19 controls whether or not a question mark is printed. The PRINT is needed to move the cursor to the next line. I hope this method is useful to your readers. Louis M. Rastelli Thanks for the tip.
Reading Trivia RandomlyHow can I READ random items in DATA statements to make a trivia program? Dan Lackey Reading from DATA statements is sequential, which means the computer starts reading at the beginning and continues until there are no more DATA statements. It's possible to reset some pointers, to jump into the middle of a group of DATA statements (see "Hints & Tips" in the September issue for more details), but there's a much easier way. First, put all the data into an array. An array is like a numbered list. You can then pick a number at random and look up that item in the array. Here's a simple trivia program:
10 READA$:IFA$<>"END"THENT=T+1:GOTO10 Each DATA statement has one question followed by a comma and the answer. You can add to or change the data as you like, as long as the last statement contains an END. Line 10 READs through all the DATA statements until it finds 'END." Line 20 DIMensions the array according to how many questions and answers were found in line 10. In line 30 the two-dimensional array is filled with the questions (Q$(JO)) and answers (Q$(JI)). Line 40 prints a question, 50 is a delay loop (time enough for someone to call out an answer), and line 60 prints the answer. Add a scoring routine and some more questions and you'll have a workable trivia program.
VIC ExpansionI've been looking for 8K or 16K memory expansion for my VIC-20. A mail order company lists them but doesn't have them in stock. The May GAZETTE requires at least 8K for the VIC programs. Where can I find VIC memory? I don't want a 64. Robert Day We called the toll-free Commodore customer support line at 800-247-9000. They said Commodore has VIC expanders in stock, and you can order directly from Commodore in Pennsylvania (or ask your Commodore dealer to order for you). Also, some stores still carry 8K and 16K VIC memory expanders.
One Letter At A TimeI write programs that use a lot of printed messages. In several commercial programs I have seen messages that are printed letter by letter, which looks better than just having messages appear. How would I add this feature to a program? Kevin Smith What you're asking for is fairly easy to do with the MID$ function and a delay loop. MID$ breaks a string into a smaller string. For example, N$ = "ABCDEFG": PRINT MID$(N$,2,3) would print "BCD" because the MID$ function started at the second position within N$ and continued for three characters. To pullout individual characters, use a I as the second number. Here's the subroutine you need:
10 A$="LETTERS ONE BY ONE":GOSUB500 Whenever you want to print a string one letter at a time, put it into A$ and GOSUB 500. Change the length of the K loop for longer or shorter delays. If you're feeling ambitious, you could add a short sound after you print each letter and a random length delay loop, to make it sound like a typewriter.
Adding And Subtracting Line FeedsI typed in one of your programs that allows you to print out the results. The problem is that everything prints on the same line. The paper doesn't advance. How can I add a line feed instruction? Joseph O'Keefe I own a daisywheel printer. Regardless of the software I use, I'm unable to print a spreadsheet or letter without it being double spaced. Is it possible to suppress the extra linefeed? Ronald J. Belanger The problem of too many or too few linefeeds is fairly common. To fix it, you'll have to adjust one of the DIP switches on your interface. Check the interface manual for the exact settings. Pressing RETURN causes the screen cursor to move to the beginning of the next line down. But the term "carriage return" for this action originally described the return of the printer carriage (the part that does the printing) to the beginning of a line. Some printers need two instructions: first return the carriage, then feed the paper one line up. The ASCII code for a carriage return is CHR$(13), ASCII for a linefeed is CHR$(10). But on other printers, the two actions are combined-a CHR$(13) causes a carriage return plus a linefeed. Because printers use one or the other method, most interfaces allow you to set whether or not a linefeed is added to every carriage return.
PEEKing The JoysticksI have both a VIC-20 and a 64, and would like to know how to PEEK the joystick inside a program. Patrick Toal The following statement can be used to read the value of joystick port 2 on the 64 (for port 1, change the 56320 to 56321): J=15-(PEEK(56320) AND 15) The values of J can be interpreted as follows:
0 - nothing To read the joystick fire button, use this expression (for joystick 1, change 56320 to 56321): IF (PEEK(56320) AND 10=0 THEN the fire button is pressed The VIC joystick is a little more complicated to read because one of the memory locations is needed for reading the keyboard. Use the following line to read the VIC joystick (the values of I will be the same as above): POKE 37139,0: POKE 37154,127: J-15-((PEEK(37137) AND 28)+(PEEK(37152) AND 128)/4)/4: POKE 37154,255 Use the following expression to find out if the fire button is pressed: IF (PEEK(37137) AND 32)=0 THEN the fire button is pressed The Plus14 and 16 have a built-in BASIC statement, JOY(n), to read the joysticks.
Writing Adventure GamesOne of my friends is writing a text adventure-game like Zork. I would like to write one of my own, but don't understand how to use random files. Please explain random files. Brant Phillips SAVE and other file handling commands like LOAD, OPEN, PRINT#, INPUT#, and GET# are high-level commands, because a single command does a lot of work. The computer takes care of the details like reading through memory from the beginning to the end of the program. The disk drive gets the signals, transfers them to the disk, puts a new entry in the directory, and protects the sectors used by the program. Random files, on the other hand, are low-level because you have to do all the work. They're not even really files, they're just reading and writing directly to disk. Let's say you want to create a random file. First, you would open a memory buffer in the disk drive, write to it, and copy the buffer to a sector on disk. To read it, open a buffer, copy from disk to the buffer, and read the buffer. Now things get complicated. There will be no entry in the disk directory, you'll have to remember which track and sector you used. If you accidentally choose a sector that's part of a program file, the program will be overwritten. If you choose a safe sector, it may later be scrambled by a file (because the block has not been allocated). You could use the block-allocate command (B-A), but it contains a bug. If you try to allocate a block that's already allocated, the whole track will be allocated. Random files are complicated and messy. There's no real advantage to using them in an adventure game. You'd be better off with either sequential or relative files.
49152-The Magic NumberWhy do so many of your machine language programs start with SYS 49152? Isn't it possible to use other areas of memory or SYSes? When two programs use the same locations, you can't merge or append one ML program with another to get the maximum use from your computer. G. Gorham There's nothing magic about the number 49152. You can put a machine language program almost anywhere in memory. But many machine language (ML) programmers use location 49152 because it's a safe place to put a program. The 4096 bytes of memory from 49152 to 53247 (hexadecimal $C000-CFFF) were intended to be a safe zone, BASIC doesn't use this area for anything (although many programs on cartridge use this part of memory). Locating ML programs here helps ensure that they won't get in the way of BASIC, and vice versa. Another good place for machine language is the cassette buffer, located at 828-1019 ($033C-03FB) on both the 64 and the VIC. BASIC uses the cassette buffer for temporary storage during tape operations. At other times, it's just free memory. However, this area is much smaller than the one mentioned above, and its contents are destroyed whenever the cassette drive is used. A third option is to locate a routine somewhere in the BASIC program space, which stretches from 2049 to 40959 ($0801-$9FFF) on the 64, or 4097-7679 ($1001-IDFF) on the unexpanded VIC. Using part of BASIC memory can be hazardous, though, since BASIC programs need it for storing variables. If you're careless, it's easy to crash your computer by putting ML into locations already used for something else. Or, you may cause a crash if you let part of your BASIC program (like dynamic strings) write over the machine language. You can prevent interference by carving out a protected zone for your ML program within the BASIC program area. Locations 55 and 56 hold a two byte pointer address that tells the computer where BASIC user RAM ends. By lowering the value in this pointer, you can keep BASIC from using any of the locations between your new top of memory and the "real" top of memory, Another thing you can do is move up the bottom of BASIC program memory by changing the pointers at 43 and 44. Finally, you can use an advanced method called bank switching, which lets you use the RAM memory locations underneath the BASIC or KERNAL ROM. Theoretically, you could write a program for the 64 that uses all 64K of available memory. The problem with bank switching is that since BASIC is turned off, the program must be written entirely in machine language. So ML programmers like to start programs at 49152 because the cassette buffer is often too small, BASIC RAM can be hazardous, and bank switching is complex. Your last comment points up a perennial problem-where to put ML programs (especially utilities). The memory at 49152-53247 and 828-1019 is convenient, so most ML programs are put there. If both of your favorite utilities start at location 49152, however, you probably can't use them together. If one of the programs is relocatable, you may be able to move it to a different part of memory. It's difficult to make programs completely relocatable because you have to avoid two useful instructions: JMP and JSR (similar to BASICs GOTO and GOSUB). And even if you have a relocatable program, it may interfere with the operation of the other program.
Color NybblesI think there's something wrong with the PEEK command. If I enter POKE 1024,2: POKE55296,1, a white "B" appears in the upper lefthand corner. But PRINT PEEK(55296) results in 193, 81, 241, or some other numbers. If you POKE a 1 into color memory, shouldn't PEEK show that there's a 1 there? Is the computer defective or am I doing something wrong? Austin J. Moe There are a few cases, including color memory, where PEEKing doesn't give you quite the right number. A Commodore 64 has 16 colors, numbered 0-15, so color memory is wired for only four bits rather than eight. Four bits, half a byte, is called a "nybble." When you PEEK color memory, the low nybble is correct, but the high nybble will contain random values because those four bits are not hooked up. To strip off the top four bits, enter a modified PEEK: PRINT PEEK(55296) AND15. The AND function should take care of your problems. Another instance where PEEK won't work is the SID chip (the chip that creates sound on the 64). You can't PEEK into the registers there. The POKEs to make a sound do not go to regular memory, they're fed directly into the SID chip. PEEKing that area yields numbers unrelated to the values POKEd there. You might call it write-only memory.
Moving BASIC AroundI'm writing a 64 program that uses custom characters, but the program is overrunning the character set. The Programmer's Reference Guide says the highest location for the start of a character set is 14336, but it's still not high enough in memory. I tried POKE 56,48:CLR and got an out of memory error. How can I move the bottom of BASIC up to 4096 or thereabouts so I can put the characters at 2048? Walter Wright On a 64, BASIC programs fit into memory beginning at 2049 (the "bottom" of BASIC) and ending at 40959 (the "top"). Whatever memory is left over can be used by variables. It's quite possible that your variables are interfering with the custom character set. On the VIC, 64, Plus/4, and 16, locations 43-44 point to the bottom of BASIC, while 55-56 point to the top. By POKEing a 48 into location 56 (followed by a CLR, which is necessary when you lower the top of memory), you moved the top of BASIC all the way down to 12288 (48*256), which leaves only about 10K of memory for your BASIC program. It would be preferable to leave the top of BASIC untouched and move the bottom up. This line will do just that: POKE 44,64: POKE 64*256,0: NEW Now you can load the custom characters program; the variables won't interfere with the character definitions. The beginning of BASIC has been moved to 64*256, which is 1084. If you prefer to put BASIC at 4096 (16*256), change the 64 to 16 in the two POKEs.
The Save-With-Replace BugIn your February issue you wrote about the save-with-replace bug. I too have come across the problem. I was working on a program called "ESF" when I remembered that I needed to change something in another program called "ARTILLERY." So I used save-with-replace on the current copy of "ESF", loaded "ARTILLERY," made a change, and saved-with-replace. Later, I tried to load "ESF" but got "ARTILLERY" instead. My best guess is that the program is still there, but I can't get it off the disk. Matthew Whiting The Commodore save-with-replace command (SAVE "@:filename",8) has been the focus of controversy for years. Some experts have steadfastly denied that there is anything wrong with it. There was no hard proof of a bug until now. The full details will be published in an upcoming issue of our sister magazine, COMPUTE!. Here's a brief explanation: Save-with-replace does several things. First, the new copy is saved (if there's not enough room on the disk for a complete copy of the program, you'll have problems, of course). Each filename in the directory contains a pointer that indicates where to find the program, so the directory is changed to point to the new version of the replaced program. Finally, the block allocation map (BAM) is updated. Disk sectors used by the old version are marked as free, while the sectors occupied by the new version are marked as allocated. The routine to update the BAM is where the bug happens. In certain situations, the BAM is incorrectly written back to the disk. Right after a faulty save-with-replace, the program name is in the directory, the pointer to the program is correct, and the new version is on the disk. You can load the program and even verify it. But the blocks used by the program are not allocated. The next time you save a program, it may be put into those blocks, and your previous (replaced) program is gone and cannot be recovered. If you load the directory, the number of blocks used by programs plus the number of free blocks should total 664. When the bug happens, the total is often more than 664. The roots of the problem go back to the PET dual drives (drives 0: and 1.). The disk operating system (DOS) of the 1541, a single drive, was translated and modified from the original dual drive DOS. So there's a sort of "phantom" drive I in the 1541. One expert on the Commodore DOS has said the 1541 spends half its time convincing itself that it's drive zero and not drive one. Sometimes the 1541 mistakenly sets aside a buffer for the phantom drive, which can, under certain circumstances, lead to the SAVE@ bug. There are three ways to avoid the bug, and safely save-with-replace. First, you can validate the disk after every save-with-replace. This isn't such a good solution because it often takes more time than scratching the old version and doing a regular SAVE. The second solution is to always use the "0:" prefix when you use the disk. Here are some examples:
LOAD "0:programname",8 The third solution is to turn the disk drive off and then on right before a save-with-replace. And be sure to include a zero (SAVE"@0:programname",8). Another way to reset the disk drive is to enter these two lines (they should be on separate lines, don't put them on a single line with a colon between them):
OPEN 15,8,15,"UJ0"
Seeking StatusWhere is the status register located? I don't mean the I/O status register. John McNamara It's deep inside the chip that runs your computer, it does not have a memory location you can PEEK, althou gh after a machine language program exits to BASIC, you can find the most recent value of the processor status register (P) by PEEKing 783. Individual bits of P correspond to the carry, zero, interrupt, decimal, break, overflow, and negative flags. So, if you clear the carry flag with CLC, then add two numbers to get a result that's more than 256, the carry flag (one of the bits in P) will be set afterwards, indicating a number that won't fit into eight bits (in decimal, for example, 9 + 1 = 0, with a carry of 1). Most machine language instructions directly affect the A, X, and Y registers, and many will also set or clear individual flags in P. It's sometimes necessary to preserve the processor status during a subroutine or interrupt, so there are instructions to push it on the stack (PHP) and pull it off the stack (PLP). If you'd like to read the status register, use PHP followed by PLA (push P on the stack, and pull the number back into the Accumulator).
Opening Multiple FilesWe're trying to write a farm management program for our sow herd. In order to run the program efficiently, we have to be able to have two files open at once. After a lot of research, we still don't know how to do this. Can you help? Delle deSwart Theoretically, up to ten different files can be open at the same time. But there are certain rules to follow, and there are limits. With the exception of relative disk files, once a file is open, you can read or write, but not both. In addition, certain devices have one-way communication-you can only read from the keyboard, and you can only write to a printer. If you owned two cassette drives, you could read from one and write to the other (Commodore PETs had this capability, but there's only one cassette port on the VIC, 64, Plus/4, and 16. So you can only talk (read or write, but not both) to one cassette file at a time. You can communicate with more than one disk file, though, as long as you open them with different logical file numbers and different channels. So you could OPEN 3,8,5, "0:FIRSTFILE,S,W' and OPEN 5,8,9, "0:OTHERFILE,SW' to read (INPUT#3 or GET#3) from file 3 on disk channel 5 and write (PRINT#5) to file 5 on channel 9. It's also possible to use more than one disk drive, as long as they have different device numbers. The same applies to printers (the MPS-803, for example, has a switch on the back for choosing device number 4 or 5). Relative disk files are a special case. You cannot have more than one relative file open at any one time (although you can open other types of disk channels). And, once a relative file is open, you can read and write to it.
Checking A Disk For Free BlocksHow do you find out how many blocks are left on a disk? Is there a program to access the disk drive (maybe PEEKs or POKEs) and then print the number? David Ross Here's a short routine you can add to your, program. First, it opens a file to the part of the directory ($) containing all USR files named "Z." Because there are probably no such files on your disk, the subdirectory will be empty, containing only the header (disk name) and the number of blocks free. (Incidentally, the program won't work if you do have a USR file named "Z.") Next, the first 34 bytes are thrown away, which leaves the answer in low-byte/high-byte format. In line 50, the number of blocks free (variable BF) is printed.
10 OPEN1,8,0,"$0:Z=U"
Animating SpritesHow can you make a sprite that has moving parts-a sprite person that walks along with legs that move, for example? Geoff Hill Once you've defined a sprite shape, and POKEd the information into memory, you have to tell the computer where in memory it can find the shape. The sprite shape pointers are located at 2040-2047 (corresponding to sprites 0-7). The number in 2040, times 64, is the beginning of the shape for sprite 0, for example. To create an animated sprite, you'll have to design two or more shapes for that single sprite. A walking sprite might need four shapes., 1) feet together on the ground, 2) one foot forward in the air, 3) feet apart, both on the ground, and 4) one foot behind, in the air. You could put these four shapes into 16128, 16192, 16256, and 16320 (each sprite shape needs 63 bytes). These numbers divided by 64 are 252, 253, 254, and 255. To give sprite 0 the first shape, POKE 2040,252. To give it the last shape, POKE 2040,255. With a single POKE to the sprite pointer the whole shape of the sprite changes. Cycling through the different shapes would make it look like the sprite is walking in place. If you then gradually increase the X-coordinate, it would seem to be walking across the screen.
Can You Rearrange A Directory?I would like to change the order of program and filenames in some of my disk directories. Is this possible? Or will I have to copy the files onto a new disk in the desired order? David Voelker If you scratch a program from disk and then save a different program, the new program shows up in the same place in the directory as the program that was scratched. So you could load a program, save it under a different name (to the same disk), and then scratch the original. The next program or file would go into the empty slot. But there's a simpler way-the COPY command, which makes an exact copy of a file on the same disk, under a different name. The syntax is OPEN15,8,15: PRINT#15, "CO:newname=0:oldname": CLOSE15. Let's say you have a sequential file and a program on a disk in this order:
10 "FILEl" SEQ To switch the order, enter the following commands in immediate mode:
OPEN15,8,15 First, FILE1 is copied (C0:) to a file called TEMPFILE (in the third spot on the disk). The directory now contains FILE1, PROGRAM1, and TEMPFILE, in that order. Scratching FILE1 (S0:) leaves a space open at the beginning. Next, PROGRAM1 is copied to TEMPPRG (which is now in the first slot). After the second copy, the directory should look like this:
23 "TEMPPRG" PRG Now, PROGRAM1 is scratched (leaving the second slot open), TEMPPRG is renamed (R0:) to PROGRAM1 and TEMPFILE is copied to FILE1. Finally, TEMPFILE is scratched.
Converting A Number To ASCIIHow do I convert a number to ASCII codes that can be printed? If I have a byte containing a 65 and try to print it, won't it appear as an "A"? How can I make a 65 into the characters "6" and "5"? Lonnie De Cloedt As you've noted, LDA #$41:JSR $FFD2: RTS will put an "A" on the screen, The ASCII values for "6" and "5" are 54 and 53 (hex $36 and $35). So the routine you need will have to PEEK a byte and translate it to one or more ASCII numbers. Since the number may be anything from 000 to 255, you'll need to set aside three memory locations. First, put 48s (hex $30) into the three locations because the character "0" is ASCII 48. Load the Accumulator with the number to be translated, and compare it with 100 (CMP #$64). If the carry is set, the number is higher than 99, so you can subtract 100 (SEC:SBC #$64) and increment the first of the three memory locations (representing the hundreds column). Keep comparing the number to 100 and subtracting 100 as long as the carry is set. When the carry is clear, the number will be in the range 0-99. Then do the same for the tens column: compare to ten (CMP #$0A) and if the carry is set, subtract ten and increment the tens column. When you've gotten to a number less than 10, you can just add it to the third memory location. Now print the three ASCII numbers you've generated. Not surprisingly, there's a ROM routine that translates numbers to their ASCII equivalents and prints them. To call it, load the Accumulator with the low byte, load the X register with the high byte, and JSR $BDCD on a 64 ($DDCD on a VIC). The number will print wherever the cursor happens to be.
Peculiar Vectors: A 6502 BugMachine language programmers should exercise caution when using an indirect jump on the 64. If the indirect vector crosses a page boundary, JMP ($10FF) for example, the low byte of the address will go into one page ($10FF) and the high byte into the beginning of the next page ($1100). The JMP instruction, however, will take the low byte from $10FF, and the high byte from $1000 and not $1100, as it should. In the three books I've read on the 6502, I have never seen this mentioned, and I thought your readers would appreciate this information. Kernie E. Houser This bug in the 6502 and 6510 chip affects not only the 64, but the VIC, Atari, Apple II, and any other computer built around that family of chips. Because of this quirk of the 6502, you should either avoid indirect jumps altogether or put your vectors in a place that you know is definitely not a page boundary. For readers who aren't familiar with indirect jumps, here's a brief explanation. An absolute jump is like GOTO in BASIC. JMP $C200 sends a machine language program to whatever ML program is currently at $C200. An indirect jump, signalled by an address in parentheses, does something different. The instruction JMP ($0330) does not jump to a program at $0330, it gets an address from $0330-0331 and jumps to that address. So $0330 is a vector or pointer to another routine, and an indirect jump bounces off the pointer to somewhere else in memory. |