I’ve been eager to get something displayed on the screen using 6502 assembly language. The task has been quite difficult, but I finally got it. I ended reading large sections of the following books before getting a handle on the task:
- Atari Roots by Mark Andrews (1984)
- Assembly Language Programming for the Atari Computers by Mark Chasin (1984)
And, although I read this one many times almost 30 years ago, I re-read the entire thing with special focus on the IOCB vectors:
- Mapping The Atari (Revised) by Ian Chadwick (1985)
I found the most benefit from “Assembly Language Programming for the Atari Computers”, and “Mapping The Atari”. I managed to get a single character on the screen using OS vectors documented in the latter. Full strings of text were elusive until I started reading sections of the former. I highly recommend both of these for the beginning Atari Assembly language programmer.
Printing to the screen in Assembly is not as easy as it is with BASIC. In BASIC, printing “Hello World!” is accomplished in one line of code:
10 PRINT "Hello World!"
The BASIC PRINT command handles most of the work for you such as: opening the screen, copying the string buffer to the screen, and closing the screen. In listed form this program takes up 24 bytes of disk space. Since BASIC is interpreted and not compiled I do not have a “compiled” size, however it’s 56 bytes in tokenized form (on disk).
In Assembly you have to do it all. To accomplish the task I learned about the CIO (central input/output) routines and using the IOCB (input/output control block) for the screen device. The same Hello World program in Assembly increases from one line to fifteen lines. The listed size is 878 bytes on disk and only 50 bytes in assembled form (on disk).
Here is my Hello World assembly listing:
05 .OPT OBJ 10 *= $0600 0100 ; CIO 0110 ICHID = $0340 ;IOCB 0 S: 0120 ICCOM = $0342 ;IOCB Command 0130 ICBAL = $0344 ;Xfer Buffer Adr 0140 ICBAH = $0345 0150 ICPTL = $0346 ;PutByte Adr 0160 ICPTH = $0347 0170 ICBLL = $0348 ;Buffer Len 0180 ICBLH = $0349 0190 CIOV = $E456 ; CIO Vector 0500 ; Setup CIO Call 0510 LDX #0 ;IOCB 0 0520 LDA #9 ;Put Cmd Val 0530 STA ICCOM,X ;Set it as the cmd 0540 LDA #HELLO&255 ;Str low byte 0550 STA ICBAL,X 0560 LDA #HELLO/256 ;Str high byte 0570 STA ICBAH,X 0580 LDA #0 ;Str Len low byte 0590 STA ICBLL,X 0600 LDA #$FF ;Str Len high byte 0610 STA ICBLH,X 0620 ; Call CIO 0630 JSR CIOV 0640 RTS 1000 HELLO .BYTE "Hello World!",$9B
So what is all that doing? I’ll break it down by line:
- 5: Assembler directive for Mac/65 telling it to create object output
- 10: The memory address where the program will be assembled. Page 6, this program is small.
- 110-190: Assembler equates to make coding easier.
- 510: Load the X register with 0. This is the offset reference to dictate which IOCB we want to use. S: is already open by the OS on IOCB 0, so I use it.
- 520: Load the accumulator with 9 which is the value of the IOCB “put record” command.
- 530: Store the IOCB command held in the accumulator in the IOCB command byte address which is ICCOM + the value of the X register (0).
- 540: Load the low byte of the Hello World string into the accumulator.
- 550: Store the accumulator into the IOCB buffer address low byte.
- 560: Load the high byte of the Hello World string into the accumulator.
- 570: Store the accumulator into the IOCB buffer address high byte.
- 580: Load the accumulator with 0.
- 590: Store the accumulator into the IOCB buffer length low byte.
- 600: Load the accumulator with 255 (FF).
- 610: Store the accumulator into the IOCB buffer length high byte. Buffer length will be 255 characters max.
- 630: Call the CIO routine now that all the IOCB parameters are filled out. The “put record” command will stop output at either the max length (in this case 255) or at the string EOL (end of line / return; 155 ($9B)).
- 640: Return from program
- 1000: String we want to print (Hello World!) and an EOL.
What did I learn?
It might be easiest to state by starting with an analogy. Say you want start a car. In BASIC you might do it with an easy to understand command, like:
but in Assembly you have to explicitly give all the process steps for starting the car, like:
FIND LOCATION OF KEYS SELECT CAR KEY INSERT CAR KEY INTO IGNITION APPLY BRAKE TURN IGNITION 180 DEGREES HOLD IGNITION UNTIL ENGINE STARTS RELEASE IGNITION
The difference being the instructions are executed much more efficiently, and you have much more control over the starting process. What if you needed to apply a choke, it might not be so easy in BASIC, but in Assembly you just add that step. Assembly isn’t as hard as it looks once you understand the need to break down each task into very specific steps.
And with that I have officially surpassed the Assembly knowledge I had long ago and completed a task I was never before able to do! Now its time to put screen display to work. I’m going to add some messages to my PREFS program.