The Commodore 64
From Maze
(Difference between revisions)
(Added a screenshot of the color-shifted version) | (More changes to hidden code section) | ||
| Line 8: | Line 8: | ||
After you carefully type it[2], you input "RUN". The screen erupts into motion as lines flow onto the screen. As each row fills with symbols, it starts drawing on the next line. When the screen is full, the entire maze moves up and leaves space at the bottom for new characters to fill. The result is a dynamic maze. | After you carefully type it[2], you input "RUN". The screen erupts into motion as lines flow onto the screen. As each row fills with symbols, it starts drawing on the next line. When the screen is full, the entire maze moves up and leaves space at the bottom for new characters to fill. The result is a dynamic maze. | ||
| − | [INSERT TWO FIGURES - Preferably photos of the program running on an actual C64, since we have screenshots of it all over the place before now] | + | [tk-Photos of the program running on an actual C64, since we have screenshots of it all over the place before now] |
The three lines of code that create this continually generating maze do so because of the specific design of the C64, paired with the design of the BASIC language in which the program is written. The manual explains the code element by element, first introducing the idea of a character code and then pointing out the importance of the number 205.5 specifically. The explanation of how the code works is followed by something even more valuable: an invitation to modify the code, change it, and make it your own based on what you've read. | The three lines of code that create this continually generating maze do so because of the specific design of the C64, paired with the design of the BASIC language in which the program is written. The manual explains the code element by element, first introducing the idea of a character code and then pointing out the importance of the number 205.5 specifically. The explanation of how the code works is followed by something even more valuable: an invitation to modify the code, change it, and make it your own based on what you've read. | ||
| Line 18: | Line 18: | ||
Since the chance of having a value between 205.5 and 206 is roughly equal to the chance of having a value between 206 and 206.5 the maze contains roughly an even distribution of these two graphics, giving it a balanced look. The manual suggests, “If you'd like to experiment with this program, try changing 205.5 by adding or subtracting a couple tenths from it. This will give either character a greater chance of being selected.” This suggestion increases the chances for one character to be selected over the other. Changing 205.5 to 205.1 gives the value 205 a 90% chance of drawing, which strongly affects the visual quality of the maze: | Since the chance of having a value between 205.5 and 206 is roughly equal to the chance of having a value between 206 and 206.5 the maze contains roughly an even distribution of these two graphics, giving it a balanced look. The manual suggests, “If you'd like to experiment with this program, try changing 205.5 by adding or subtracting a couple tenths from it. This will give either character a greater chance of being selected.” This suggestion increases the chances for one character to be selected over the other. Changing 205.5 to 205.1 gives the value 205 a 90% chance of drawing, which strongly affects the visual quality of the maze: | ||
| − | [INSERT FIGURE] | + | [tk-figure of 205.1+RND(1) version] |
Replacing 205.5 with 205.9 gives the value 206 a 90% chance of drawing: | Replacing 205.5 with 205.9 gives the value 206 a 90% chance of drawing: | ||
| − | [INSERT FIGURE] | + | [tk-figure of the 205.9+RND(1) version] |
Great, you have not only learned to program a maze, but you have also learned how to modify it by controlling the ratio between the "/" and "\" characters that form it. But what exactly did you change? There is no code that explicitly sets the probability of either character appearing on the screen. You never even tell the C64 that it should round off the fractional 205.x and 206.x values to whole numbers. You achieved the effect of changing the character's frequency indirectly by taking advantage of the programming environment of BASIC and the inherent properties of the Commodore 64 platform. | Great, you have not only learned to program a maze, but you have also learned how to modify it by controlling the ratio between the "/" and "\" characters that form it. But what exactly did you change? There is no code that explicitly sets the probability of either character appearing on the screen. You never even tell the C64 that it should round off the fractional 205.x and 206.x values to whole numbers. You achieved the effect of changing the character's frequency indirectly by taking advantage of the programming environment of BASIC and the inherent properties of the Commodore 64 platform. | ||
| Line 48: | Line 48: | ||
The process of choosing a random number is described elsewhere in this book, but one key feature of 10-Print's environment that makes the random number useful is its ability to automatically truncate decimals. The equation used to determine which character code to display, 205.5+RND(1), results in values between 205.500000 and 206.500000 and includes 6 digits after the decimal. Character codes, however, are integer values. In many environments the programmer would need to include a separate function to round off the equation output and create an integer that would be recognized as a character code, potentially pushing the 10-Print program beyond a single line. BASIC's CHR$() function simply truncates the decimal value, resulting in character codes of either 205 or 206. | The process of choosing a random number is described elsewhere in this book, but one key feature of 10-Print's environment that makes the random number useful is its ability to automatically truncate decimals. The equation used to determine which character code to display, 205.5+RND(1), results in values between 205.500000 and 206.500000 and includes 6 digits after the decimal. Character codes, however, are integer values. In many environments the programmer would need to include a separate function to round off the equation output and create an integer that would be recognized as a character code, potentially pushing the 10-Print program beyond a single line. BASIC's CHR$() function simply truncates the decimal value, resulting in character codes of either 205 or 206. | ||
| − | How can the assumption be changed? There is no way for CHR$() to render fractional numbers. The number CHR$() uses to display a character has to correspond to a position in the PETSCII chart, which only contains whole number indices (see sidebar for more on PETSCII). | + | ''How can the assumption be changed?'' There is no way for CHR$() to render fractional numbers. The number CHR$() uses to display a character has to correspond to a position in the PETSCII chart, which only contains whole number indices (see sidebar for more on PETSCII). |
=== Sidebar: Keyboard and Character Map, Hardware and Software === | === Sidebar: Keyboard and Character Map, Hardware and Software === | ||
| Line 77: | Line 77: | ||
Even before a user begins typing 10-Print into their Commodore 64, it has set an aspect of the environment that will greatly effect the program's output: the default text and background colors of the Commodore 64. While these defaults can be overridden easily enough – text colors in BASIC on the Commodore 64 are printable characters so adding PRINT CHR$(30), for example, would make the text green; background colors are similarly changeable using POKE – the original 10-Print program outputs using the pre-existing conditions of the environment. | Even before a user begins typing 10-Print into their Commodore 64, it has set an aspect of the environment that will greatly effect the program's output: the default text and background colors of the Commodore 64. While these defaults can be overridden easily enough – text colors in BASIC on the Commodore 64 are printable characters so adding PRINT CHR$(30), for example, would make the text green; background colors are similarly changeable using POKE – the original 10-Print program outputs using the pre-existing conditions of the environment. | ||
| − | How can the assumption be changed? Add a pair of lines that set the text and background colors: | + | ''How can the assumption be changed?'' Add a pair of lines that set the text and background colors: |
<pre>5 PRINT CHR$(30) | <pre>5 PRINT CHR$(30) | ||
6 POKE 53281, 0 | 6 POKE 53281, 0 | ||
| Line 90: | Line 90: | ||
''Set text color to light blue''<br /> | ''Set text color to light blue''<br /> | ||
'''''Divide screen into a 40 column by 25 row grid'''''<br /> | '''''Divide screen into a 40 column by 25 row grid'''''<br /> | ||
| − | 10 PRINT CHR$(205.5+RND(1)); : GOTO 10</blockquote> | + | 10 PRINT CHR$(''round down(''205.5+RND(1)'')''); : GOTO 10</blockquote> |
| − | While the code for 10-Print is devoted to choosing a character and printing it on the screen, it says nothing about where on the screen the chosen character should appear. Instead it depends on the Commodore 64's screen memory map, a system that divides the screen into a grid 40 characters wide and 25 characters tall while in text mode. Each memory location has an associated pixel position on the screen, and any character value that is stored in that memory slot will be displayed at the corresponding screen position. | + | While the code for 10-Print is devoted to choosing a character and printing it on the screen, it says nothing about where on the screen the chosen character should appear. Instead it depends on the Commodore 64's screen memory map, a system that divides the screen into a grid 40 characters wide and 25 characters tall while in text mode. Each memory location controls an 8x8 pixel grid, the location of which is determined by the order of the memory slots. The first slot, 1024, is at grid location 0,0 and pixel location 0,0. Memory location 1025 controls grid location (1,0) and is drawn at pixel location (8,0), and so on. Any character value that is stored in a memory slot will be displayed at the corresponding screen position. The large border that surrounds the maze is not addressable in the screen grid; it is required by the wide variation within cathode ray tube (CRT) televisions of the era. The CRT screen of different televisions framed the pixels differently, making only a subset of pixels in the center reliable for display. The 32 pixels borders on the left and right and 35 pixel borders on the top and bottom reduce the usable display to 320 wide by 200 pixels high. |
| − | Modified version: Not possible in text mode | + | ''How can the assumption be changed?'' In text mode, the screen grid is fixed. However, the Commodore 64 can also be put into bit map mode where each pixel on the screen can be addressed (see sidebar). |
| + | |||
| + | [tk-Somebody with more experience in using raster mode than me (John) might want to write this; alternatively, I can come back to it when I have more time for research.] | ||
===Cursor=== | ===Cursor=== | ||
| Line 102: | Line 104: | ||
''Divide screen into a 40 column by 25 row grid''<br /> | ''Divide screen into a 40 column by 25 row grid''<br /> | ||
'''''Move cursor to column 0'''''<br /> | '''''Move cursor to column 0'''''<br /> | ||
| − | 10 PRINT CHR$(round down(205.5+RND(1)));<br /> | + | 10 PRINT CHR$(''round down(''205.5+RND(1)'')'');<br /> |
<blockquote> | <blockquote> | ||
'''''Move cursor 1 column to the right'''''<br /> | '''''Move cursor 1 column to the right'''''<br /> | ||
| Line 112: | Line 114: | ||
Once a grid has been established the software still needs to determine the grid position in which it should be drawing a character. Without this information the characters would be drawn either randomly across the screen or one on top of another instead of in the neatly linear pattern seen in 10-Print. The Commodore 64's text mode includes the concept of a cursor that begins on the left side of the screen, advances to the right by one column every time a character is printed to the screen, and drops to the next row while returning to the leftmost column after the final column is filled. The key command here is PRINT; if the characters were simply POKE'd to the screen, the cursor would not be moved and the environment would not automatically handle positioning. The semi-colon at the end of the PRINT statement ensures that a line break will not be inserted between each character, allowing the cursor to move horizontally despite only one character being printed in each iteration of the loop. | Once a grid has been established the software still needs to determine the grid position in which it should be drawing a character. Without this information the characters would be drawn either randomly across the screen or one on top of another instead of in the neatly linear pattern seen in 10-Print. The Commodore 64's text mode includes the concept of a cursor that begins on the left side of the screen, advances to the right by one column every time a character is printed to the screen, and drops to the next row while returning to the leftmost column after the final column is filled. The key command here is PRINT; if the characters were simply POKE'd to the screen, the cursor would not be moved and the environment would not automatically handle positioning. The semi-colon at the end of the PRINT statement ensures that a line break will not be inserted between each character, allowing the cursor to move horizontally despite only one character being printed in each iteration of the loop. | ||
| − | Modified version: | + | ''How can the assumption be changed?'' Instead of using PRINT; use POKE. Unlike PRINT, POKE has no concept of a cursor and only writes a specific value to a specific location in the screen grid's memory block. Note the side effect seen in the screenshot below, however: the two characters of the maze are masked in the foreground color, with the background color showing the characters normally displayed. [tk-Why?] |
<pre>10 POKE 1024+RND(1)*1024,205.5+RND(1): GOTO 10</pre> | <pre>10 POKE 1024+RND(1)*1024,205.5+RND(1): GOTO 10</pre> | ||
| + | |||
| + | [[File:10-print-poke.gif|600px|thumb|center|10-Print using POKE instead of PRINT]] | ||
===Scrolling=== | ===Scrolling=== | ||
| Line 121: | Line 125: | ||
''Divide screen into a 40 column by 25 row grid''<br /> | ''Divide screen into a 40 column by 25 row grid''<br /> | ||
''Move cursor to column 0''<br /> | ''Move cursor to column 0''<br /> | ||
| − | 10 PRINT CHR$(''round down''(205.5+RND(1)));<br /> | + | 10 PRINT CHR$(''round down(''205.5+RND(1)'')'');<br /> |
<blockquote> | <blockquote> | ||
''Move cursor 1 column to the right''<br /> | ''Move cursor 1 column to the right''<br /> | ||
