Each byte is composed of eight bits, each of which can be either a one (on) or a zero (off). The alterable area of memory you use for your programs is called the Random Access Memory (RAM), while the area used by the Atari to run things is called the Read Only Memory (ROM). Although some of the memory locations in the special Atari chips were designed to be written to like RAM, the rest of ROM, including the Operating System ROM, cannot be altered by you since it contains routines such as the floating point mathematics package and the input/output routines.
I hope that the reader is familiar enough with his or her Atari to understand some of these rudimentary uses of a memory map. It is not the scope of this manual to fully explain how to use PEEK and POKE statements; refer to your BASIC manual. Briefly, however, PEEK allows you to look at the value stored in any one memory location. If you want that value to be printed to the screen, you must preface the PEEK statement with a PRINT statement such as:
PRINT PEEK (708)
If you haven't changed your color registers, this will return the number 40 to your screen. All bytes in the Atari can hold a number between zero and 255. POKE allows you to place a value into a byte, such as:
POKE 755,4
By doing this you will have turned your text upside down! You can return it to normal by:
POKE 755,2
Similarly, POKE 710,80 will turn your screen dark purple! As with PEEK, POKE can only involve numbers between zero and 255. You will not be able to POKE into most of the ROM locations since the numbers in many of them are "hard-wired," "burned" into the chip, and cannot be changed in this manner.
So how does the Atari (or other eight-bit microcomputers, for that matter) store a number larger than 255? By breaking it downinto two parts; the Most Significant Byte (MSB), which is the number divided by 256 and rounded down to the nearest whole number, and the Least Significant Byte (LSB), which is the original number minus the MSB. The Atari knows to multiply the MSB by 256 and add the LSB to get the number. For example, the number 45290 is stored as two parts: 234 (LSB) and 176 (MSB). 176 times 256 equals 45056, plus 234 equals 45290.
BYTE = PEEK(88) + PEEK(89) * 256
The reverse (to break up a decimal location into MSB and LSB) is done by:
MSB = INT(BYTE/256):LSB = BYTE -MSB * 256
This process is easier for assembly language programmers who use hexadecimal numbers, since the right two digits are always the LSB and the two left of them are the MSB. For example:
$D016 (hexadecimal for 53270) equals 16 (LSB) and D0 (MSB)
$16 equals 22 in decimal, and $D0 equals 208 in decimal. Multiply the MSB by 256 and add 22 and you get 53270. Throughout the map portion of this book I have provided both decimal and hexadecimal numbers together for ease of reference. In 8K BASIC, you can use decimal numbers only with POKE, and PEEK will return only decimal values to you.
Hexadecimal is a base 16 used instead of the normal base ten system because it is more suited to the eight-bit structure of the computer. So, when we say 2175 in decimal, what we really mean is:
10000 1000 100 10 1
0 2 1 7 5
In hex, the same number is $87F. That breaks down to:
4096 256 16 1 0 8 7 F
Rather than multiply each next step up by ten, we multiply by 16. Okay, but where do we get "F" from? Well, if base ten has the numbers zero to nine, base 16 will have to have some letters added to the end to make up for the extra numbers:
Decimal 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 Hex 0 1 2 3 4 5 6 7 8 9 A B C D E F
So $F equals 15 in decimal. Now here's how it all relates to binary math and bits:
Each byte can be broken up into two parts (nybbles), like this:
0000 0000
If each nybble is considered a separate number, in decimal, the value of each would range from zero to 15, or zero to $F. Aha! So if all the bits in each group are on (one, or set), then you have:
1111 1111 Binary 15 15 Decimal F F Hex
You join the two hex numbers togethere and you get $FF (255 in decimal), the largest number a byte can hold. So you can see how we translate bytes from binary to hex, by translating each nybble. For example:
1001 1101 Binary 9 13 Decimal 9 D Hex
$9D equals nine times 16 plus 13, or 157 in decimal.
0100 0110 Binary 4 6 Decimal 4 6 Hex
$46 equals four times 16 plus six, or 70 in decimal.
1111 1010 Binary 15 10 Decimal F A Hex
$FA equals 15 times 16 plus ten, or 250 in decimal.
Obviously, it is easier to do this with a translation program or a calculator!
Since I will often be discussing setting bits and explaining a small amount of bit architecture, you should be aware of the simple procedures by which you can turn on and off specific bits in any location (that is, how to manipulate one of the eight individual bits within a byte). Each byte is a collection of eight bits: numbers are represented by turning on the particular bits that add up to the number stored in that byte. Bits can be either zero (0 equals off) or one (1 equals on, or SET). The bits are numbered zero to seven and represent the following decimal numbers:
Bit 7 6 5 4 3 2 1 0 Value 128 64 32 16 8 4 2 1
The relationship between the bits and the powers of two should be obvious. Adding up all the numbers (all the bits are set) gives us 255. So each byte can hold a number between zero (no bits are set) and 255 (all bits are set).
Sometimes, instead of zero, no bits set is intended to mean 256. That will be noted in the relevant locations. So how do you set a bit? Simple: POKE it with the appropriate number. For example, to set Bit 5, POKE the location with 32. To set Bits 7, 5 and 4, add up their values, 128 + 32 + 16, and POKE the location with the total: 176.
Sometimes you need to set a bit without changing other bits already set, so you:
POKE number, PEEK(number) + decimal value for the bit to be set. (i.e., POKE 50418, PEEK(50418) + 32)
To turn off a bit, instead of adding the value you would subtract it with POKE number, PEEK(number), minus the decimal value for the bit to be turned off. Binary math is simple and easy to learn; if you don't understand it now, you should do further reading on machine language before attempting any serious use of this guide.
AND is usually used as a mask - to zero out unwanted bits. You compare two binary numbers using AND; if both bits in the same location are one, then the result is one. If either bit is zero, then the result is zero. For example:
51 = 00110011
AND 15 = 00001111
Result = 00000011 = 3
OR is frequently used to force setting of a bit. If either bit in the original or mask is one, then the result is one. For example:
65 = 01000001
OR 128 = 10000000
Result = 11000001 = 193
In this case, 65 is the ATASCII "A". By ORing it with 128, we get 193, the ATASCII inverse "A".
EOR "flips" bits in the original if the mask has a one in the same location. For example:
193 = 11000001
EOR 128 = 10000000
Result = 010000001 = 65
In this case, we have returned the inverse "A" to the normal ATASCII value. An EOR with 255 (all ones) will produce the complement of the number:
171 = 10101011
EOR 255 = 11111111
Result = 01010100 = 84
In brief:
Original: Mask: AND: OR: EOR:
0 0 0 0 0
0 1 0 1 1
1 0 0 1 1
1 1 1 1 0
Atari BASIC supports AND, OR and NOT; NOT is the logical complement where NOT1 equals zero and NOT0 equals one. If the expression is true, you get a zero; if NOT true, a one is returned - for example, NOT ((3+4) > = 6) results in zero. See COMPUTE!, May 1981 for a machine language routine to allow you to perform Boolean bit logic using a USR call from BASIC.
In general, I have attempted to avoid using 6502 assembly language mnemonics, but have included them where I felt their use described the action to be taken better than a lengthy explanation. Most common are JMP (jump to location), JSR (jump to subroutine), RTS (return from subroutine), and RTI (return from interrupt). Readers should be minimally familiar with machine language in order to understand any machine language subroutines used here.
I also suggest that if the reader doesn't already have one, he or she obtain a program to translate hex to decimal and decimal to hex (possibly even one with binary translationa as well.). The ROM cartridge from Eastern House Software, Monkey Wrench, is useful for this purpose. Perhaps the easiest to use is the TI Programmer calculator from Texas Instruments.
The examples in this book were all written using Atari 8K BASIC. They are intended to demonstrate the use or the effect of a particular memory location. They are not intended as the best examples of BASIC programming; they were written for simplicity, not sophistication.
As a final note, any question or doubt as to either a particular location or explanation has been noted. It can't hurt to play around yourself, POKEing in the memory to see what other effects you can discover. If you find something I didn't, good! Please write and let me know. You can't hurt the machine by POKEing about in memory, although you may crash any program in memory, so SAVE your program first. Usually you can salvage it by pushing RESET, but you may have to turn off the machine and reboot on occasion. You can learn a lot about
BASIC is a good language to start with: you can use it to learn programming, to explore your computer, to experiment with, and to have fun with. However, when you are ready to go on, you will have to learn a more efficient, faster language if you really want to make the best use of your Atari. Many people choose 6502 machine language because of its speed.
If you want to stay with a high-level language, I suggest you learn FORTH. It has some of the speed of machine language code with the ease of "higher level language" programming.
Computer languages, whichever you use, are quite exact in their meaning, especially compared to English. Consider that in English, a fat chance and a slim chance both mean the same thing. Yet POKE, PUT, and PUSH have very different meanings in computerese.
(R) Read (W) Write
Sometimes the functions are different in a particular location, so each is noted.
(D:) Disk Drive
(E:) Screen Editor
(S:) Display
(K:) Keyboard
(P:) Printer
(C:) Cassette
(R:) RS-232 interface. (Don't confuse this with (R) for Read.) The
context should be obvious.
(number) e.g. (708) Shadow Register. This is a RAM register which corresponds to a ROM register in one of the special Atari chips such as GTIA or POKEY. The shadow location is the address you use to PEEK and POKE values. These shadow locations are polled by the hardware addresses 30 times a second at every stage two VBLANK interval, and the values are transferred to the hardware locations for use. In order to effect any "permanent" change to the hardware location, you have to use the shadow register BASIC (of course, every change is negated when you turn the machine off!). Only machine language is fast enough to use the hardware addresses directly.
For example, location 54273 is for character control. It polls location 755 to see if the screen characters are to be normal, inverse, or upside down. To change the characters, you POKE location 755 - the shadow - not 54273. If you POKE location 54273, you will get the desired effect - for 1/60 of a second! As mentioned above, you can use the hardware addresses directly in machine language, but not in BASIC. It's just too slow.
Sometimes, where most appropriate, a hexadecimal number will be displayed and the decimal number put in parenthesis. The context should be obvious concerning which is a shadow or a decimal number.
(*letter) refers to a source in the case of a conflicting location or explanation. See the source below.
($number) refers to a hexadecimal (also called hex) number (i.e.: $D40E). I also refer to "pages" in memory. Pages are sections of 256 bytes ($100) of memory which end with 00 (i.e.: $E200, $C000, $600). Four pages ($400) equals 1024 bytes or 1K (kilobyte) of memory.
Although people often refer to the entire ROM area
as the OS, this is not correct. The OS ROM is that portion of memory which holds
the floating point package, The Atari character set, the device handlers, and
both CIO and SIO. The actual operating system itself is the portion of the OS
ROM which handles the I/O.
With both RAM and ROM, we refer to areas with lesser values as being in
"low" memory and locations with larger values as being in
"high" memory.
(*M) Master Memory Map Ver. 2, Santa Cruz Educational Software, 1981. A memory guide by the same people who brought us the TRICKY TUTORIAL series. The latter are both tutorials and applications utilities. The map does contain some annoying errata.
(*Y) Your Atari Computer, by Lon Poole with Martin McNiff & Steven Cook, Osborne/McGraw-Hill, 1982. The best guide to date on general use of the Atari. Very highly recommended.
(*C) COMPUTE!'s First Book of Atari, by the Editors of COMPUTE! Magazine, Small System Services Inc., 1981. A good collection of early articles that appeared in the magazine.
At the time of this writing, COMPUTE!'s Second Book of Atari had just been released. It is therefore not used as a reference source here, but it is a must for serious programmers. It contains a wealth of information on an enormous range of topics, including advanced graphics, forced-read modes, page flipping, Atari BASIC and many valuable utilities. It should be a staple in most Atari owners' libraries.
(*I) Inside Atari DOS, compiled by Bill Wilkinson, published by COMPUTE! Books , Small System Services, Inc., 1982. An explanation and copyrighted source code for the FMS portion of DOS 2.0.
Atari BASIC: Learning by Using, by Thomas Rowley, Hofacker Press, 1981. A lot of information packed into a surprisingly good little book.
The following publications are all from Atari, Inc. I recommend them to all truly interested in understanding their Atari computers:
(*D) De Re Atari: an arcane, but indispensible reference to the Atari's operations and some of its most impressive aspects, by Chris Crawford et al. Serialized in BYTE magazine, late 1981 to mid 1982. Earlier editions have some errata, so make sure you obtain the latest edition.
(*O) Operating System User's Manual and
(*H) Hardware Manual. The famous "technical manuals" pair.
Indispensible for serious users, albeit heavy going and not generally very professional
in their presentation of material.
(*8) 850 Interface Module Operator's Manual. The 850 manual gives many examples in BASIC of how to use the RS232 serial interface ports for both printer control and telecommunications. A very good terminal program called Jonesterm, in BASIC with matching machine language subroutines, is in the public domain and is available on many electronic bulletin board systems, including CompuServe. Modem users will find many useful programs available in CompuServe.
(*L) Operating Systems Listing and
(*U) Disk Utilities Listings are the commented, copyrighted source code listings
for the OS and the DUP.SYS portion of DOS.
(*B) Atari BASIC Reference Manual.
(*S) Disk Operating System II Reference Manual. (*A) Atari Microsoft BASIC Instruction Manual. Microsoft BASIC makes excellent use of PEEKs and POKEs to accomplish many tasks. It also has many powerful commands not available in the 8K BASIC.
I found a number of other magazine articles useful, particularly those in COMPUTE! and Creative Computing. I also found Softside, BYTE, ANALOG and Micro magazines to be useful in the preparation of this book. these are all referred to throughout the book by month or issue.
We owe a vote of thanks to the folks at Atari who published the technical manuals and the source listings of the operating system and the DOS. We owe another vote of thanks to Bill Wilkinson, of Optimized Systems Software Inc., who created the DUP portion of DOS and decided to publish the source code in his Inside Atari DOS. No other computer manufacturer has, to my knowledge, ever provided users with such in-depth material or the details of its own operating systems. Without it, none of this would have been possible: a lot of the information here was gleaned from those sources.
This book is arranged in four sections: a numerical listing of the main Atari memory locations, their labels and their use; a generanl map diagram to show how the memory is broken down; an appendix of utility material with charts and tables, and an index/cross-reference guide.
There is an awful lot of information contained here; tedious as it might appear, I suggest that you read this manual through at least once. Some of the information which is not clear in one area may be elaborated on and made clearer in another area. Wherever another location is referred to in one description, you should turn to the reference and read it after you have read through the first location. You should also refer to the locations used in any sample program. The more familiar you are with the memory, the more you will get out of your Atari. When you read the description in any memory location, make sure you refer to either the shadow or hardware register mentioned, for more information.
Determine the highest RAM address and clear all RAM to zeroes (except locations zero to 15; $0 to $F).
Erase and format the device table.
Set the screen to GRAPHICS mode zero, 24 lines by 40 columns; set screen margins.
Initialize the cartridge(s) if present; test for the B (right), then for the A (left) cartridge.
Check the cartridge slots for disk boot instructions and, if they are present, boot disk.
Transfer control to the cartridge or booted program.
Initialize the RAM interrupt vectors at 512 to 548 ($200 to $224).
Store zero in the following hardware registers: 53248 to 53503, 53760 to 54527 ($D000 - $D0FF, $D200 - $D4FF).
The START key flag is tested and, if set (the START key is held down), CKEY (74;$4A) requests a cassette boot.
HATABS (794; $31A) is initialized to point to the ROM-resident device handlers.
IOCB zero is OPENed to device E:.
Coldstart (powerup) essentially wipes the computer clean and should only be used for such. It's rather drastic.
Set the warmstart flag (location 8) to true (255;$FF).
Clear the OS portion of RAM from locations 16 to 127 ($10 - $7F) and 512 to 1023 ($200 - $3FF).
Reset all RAM interrupt vectors.
Reformat the device handler table (HATABS); added vectors are lost.
Re-initialize the cartridge(s). Return to GRAPHICS mode zero.
Transfer control to the cartridge or booted program.
Restore the default values in RAM.
Note that a RESET does not wipe RAM, but leaves it intact. Usually your program and variables will be secure when you press RESET. This is considerably less drastic than powerup as above.
There are two vectors for initialization so that these processes may be user initiated: 58484 ($E474) for RESET and 58487 ($E477) for powerup.
See the OS User's Manual, pages 109 to 112, and De Re Atari for a flowchart of the process.