INSIGHT: Atari
Bill Wilkinson
Machine Language Graphics: The Final Installment
This month I will finally show you the machine language equivalents of the most important BASIC screen I/O operations. We'll begin with an example. Suppose we wanted to implement a GRAPHICS 7 statement. From two months ago, we know that the equivalent low-level statements are
CLOSE #6 OPEN #6,12 + 16,7,"S:"
A direct translation into machine language follows.
;GRAPHICS 7 ;CLOSE IOCB 6 LDX #$60 ; IOCB number LDA #12 ; the CLOSE command STA ICCOM,X ; put in place JSR $E456 ; call CIO ;OPEN IOCB 6 LDX #$60 ; IOCB number LDA #3 ; the OPEN command STA ICCOM,X ; put in place LDA #12 + 16 ; give it the same ; value STA ICAX1, X ; as you would in ; BASIC LDA #7 STA ICAX2, X LDA #DEVICE&$FF ; don't worry why STA ICBAL, X ; this works LDA #DEVICE/$100; it just does. STA ICBAL + 1, X JSR $E456 ; do the real work
Don't bother assembling this code yet—it won't work without some of the help given later in this article.
Now, if you all you ever wanted to do was emulate GRAPHICS 7, that would be an adequate method. But in BASIC, the general form of the command is GRAPHICS mode, where mode is any numeric variable or expression or your choice. It would be better if we could emulate that in machine language. And, to some degree, we can.
In BASIC's GRAPHICS statement, the mode value is called a parameter to the operation. In machine language, we also use parameters. With the 6502 microprocessor that Atari machines use, we usually try to pass the parameters in one or more of the three registers that the chip possesses: the A register (also called the accumulator), the X register, and the Y register. Suppose you need to pass an IOCB number. Since it needs to be in the X register for the call to CIO anyway, why not pass it there?
The listing that follows is not a program in and of itself. Rather it is a set of subroutines that your program may call (via JSR) to implement the given operation. At the very end of the article you will find a sample program that calls these subroutines.
When you use the subroutines in your own programs, you must note carefully the description of the parameters that I have given. Be sure that the appropriate registers contain the proper values before you jump to a subroutine.
The listing is given without line numbers. Some assemblers use line numbers, but, when they do, it's for editing purposes only—the numbers have no effect on the program. Comments are preceded by a semicolon. You may omit any of them that you like. When you have typed all this in (and have checked it carefully for errors—one mistake can cause a lockup), you should save it (or LIST it, depending upon your assembler) to disk or tape. You can then use it as the nucleus of your own graphics programs.
; ; Equates ; ; Without these, the program won't assemble properly ; ICCOM = $342 ; the COMMAND byte in the IOCB ICBAL = $344 ; the low byte of the buffer address (filename) ICBLL = $348 ; the low byte of the buffer length ICAX1 = $34A ; auxiliary byte 1: type ICAX2 = $34B ; auxiliary byte 2: mode ; CIO = $E456 ; Central Input/Output routine ROWCRS = 84 ; ROW CuRSor—y position COLCRS = 85 ; COLumn CuRSor—x position ATACHR = 763 ; where line color goes for DRAWTO ; ; Now the working routines ; ; REMEMBER: these are only subroutines ; You must call them via JSR from your own code ; ; ; CLOSE channel ; ; Parameter: X register holds IOCB number ; On exit: Y register holds error code ; CLOSE LDA #12 ; close command STA ICCOM,X ; in place JMP CIO ; do the real work ; ; ; OPEN channel,type,mode,file ; ; Parameters: X register holds IOCB number ; A register holds type ; Y register holds mode ; the address of the file/device ; name must already be set up ; in the IOCB– ; On exit: Y register holds error code ; OPEN STA ICAX1,X ; the type value TYA STA ICAX2, X ; and the mode, if appropriate LDA #3 ; OPEN command STA ICCOM, X ; in place JMP CIO ; the real work ; ; ;GRAPHICS mode ; ;Parameter: A register holds desired mode ;On exit: Y register holds error code ; GRAPHICS PHA ; save the mode for a moment LDX #$60 ; always use IOCB #6 JSR CLOSE ; be sure it is closed LDX #$60 ; the same IOCB again LDA #SNAME&$FF; the "S:" device name STA ICBAL,X ; must be put in place LDA #SNAME/$100 ; before we go further STA ICBAL + 1,X ; (take this part on faith) PLA ; recover the GRAPHICS mode TAY ; put it where OPEN wants it AND #16 + 32 ; isolate the text window and no-clear bits EOR #16 ; flip state of the text window bit ORA #12 ; allow both input and output JMP OPEN ; do this part of the work ; ; ;PUT channel,byte ; ;Parameters: A register holds byte to output ; X register holds channel number ; On exit: Y register holds error code ; PUT TAY ; save the byte here for a moment LDA #0 STA ICBLL,X ; $0000 to length STA ICBLL + 1,X ; as noted last month LDA #11 ; the command value STA ICCOM,X TYA ; data byte back where CIO wants it JMP CIO ; ; ; byte = GET( channel ) ; ; Parameter: X register holds IOCB number ;On exit: A register holds byte from GET call ; GET LDA #0 STA ICBLL, X ; $0000 to length… STA ICBLL + 1,X ; as noted last month LDA #7 ; the command value STA ICCOM, X ; where CIO wants it JMP CIO ; believe it or else, that's all ; ; ;PLOT x,y,color ; ; Parameters: A register holds color ; X register holds x location ; Y register holds y location ; NOTE: not for use with GR.8 or GR.24 ; PLOT STX COLCRS ;see my August column STY ROWCRS ;these are just POKEs LDX #$60 ;the S: graphics channel JMP PUT ;color is already in A ; ; ;byte = LOCATE( x,y ) ; ;Parameters: X register holds x location ; Y register holds y location ;On exit: A register holds color of point at (x,y) ; LOCATE STX COLCRS ;again, see column STY ROWCRS ;from two months ago LDX #$60 ;the S: graphics channel JMP GET ;color returned in A ; ; ;DRAWTO x, y, color ; ;Parameters: A register holds color ; X register holds x location ; Y register holds y location ; NOTE: not for use with GR.8 or GR.24 ; DRAWTO STX COLCRS ; once more: see the article STY ROWCRS ; from two months ago STA ATACHR ; location 763, also in that article LDX #$60 ; again, we use IOCB #6 LDA #17 ; the XIO number for DRAWTO STA ICCOM,X ; is actually the command number JMP CIO ; and that's all we really need to do
Now that we have these routines, how do we use them? A full explanation would need at least the beginnings of a tutorial book. But here's a short example. First, a small program in BASIC:
100 GRAPHICS 3 + 16 110 COLOR 2 : PLOT 10, 10 120 COLOR 3 : PLOT 20, 20 130 COLOR 1 : PLOT 0, 15 : DRAWTO 30, 15 140 GOTO 140 : REM (just wait for RESET)
Now the same thing in machine language, using the routines of the program above. The only decision you will have to make is where in memory to place the assembled code. My first line reflects what should be a safe choice for most assemblers used in most 48K byte (or 64K byte) machines. If your assembler has a SIZE or MEMORY command, use it to get an idea of what is safe. In any case, LIST or SAVE your code to disk or tape before assembling, just in case.
*= $6000 ; my "usually safe" location ; START LDA #3 + 16 ; first JSR GRAPHICS ; emulate GRAPHICS 19 ; LDX #10 ; now do PLOT 10,10 LDY #10 ;(x and y locations) LDA #2 ;with COLOR 2, a slight JSR PLOT ;change from BASIC, but close ; LDX #20 ;similarly: LDY #20 ;we want PLOT 20,20 LDA #3 ;with COLOR 3 JSR PLOT ;one call does it all ; LDX #0 ;last PLOT: LDY #15 ;PLOT 0,15 LDA #1 ;with COLOR 1 JSR PLOT ; ; LDX #30 ;and now the DRAWTO: LDY #15 ;DRAWTO 30,15 LDA #1 ;still with COLOR 1 JSR DRAWTO ;the routine does the work ; LOOP1 JMP LOOP1 ;(loop here until RESET is pressed) ; ;Append all of the code for all of; the subroutines here ; ;Your assembler may need .END or END as ; the very last line ;