]> git.sur5r.net Git - cc65/blob - libsrc/apple2/apple2-40-40-16.s
New apple2 tgi driver from Stefan Haubenthal
[cc65] / libsrc / apple2 / apple2-40-40-16.s
1 ;
2 ; Graphics driver for the 40x40x16 mode on the Apple II
3 ;
4 ; Stefan Haubenthal <polluks@sdf.lonestar.org>
5 ; Based on Maciej Witkowiak's line and circle routine
6 ;
7
8         .include        "zeropage.inc"
9
10         .include        "tgi-kernel.inc"
11         .include        "tgi-mode.inc"
12         .include        "tgi-error.inc"
13         .include        "apple2.inc"
14
15         .macpack        generic
16
17 ; ------------------------------------------------------------------------
18 ; Constants
19
20 H2              = $2C
21 PLOT            = $F800
22 HLINE           = $F819
23 CLRTOP          = $F836
24 SETCOL          = $F864
25 SCRN            = $F871
26 SETGR           = $FB40
27
28 ; ------------------------------------------------------------------------
29 ; Header. Includes jump table and constants.
30
31 .segment        "JUMPTABLE"
32
33 ; First part of the header is a structure that has a magic and defines the
34 ; capabilities of the driver
35
36         .byte   $74, $67, $69           ; "tgi"
37         .byte   TGI_API_VERSION         ; TGI API version number
38 xres:   .word   40                      ; X resolution
39 yres:   .word   40                      ; Y resolution
40         .byte   16                      ; Number of drawing colors
41         .byte   1                       ; Number of screens available
42         .byte   8                       ; System font X size
43         .byte   8                       ; System font Y size
44         .res    4, $00                  ; Reserved for future extensions
45
46 ; Next comes the jump table. Currently all entries must be valid and may point
47 ; to an RTS for test versions (function not implemented).
48
49         .word   INSTALL
50         .word   UNINSTALL
51         .word   INIT
52         .word   DONE
53         .word   GETERROR
54         .word   CONTROL
55         .word   CLEAR
56         .word   SETVIEWPAGE
57         .word   SETDRAWPAGE
58         .word   SETCOLOR
59         .word   SETPALETTE
60         .word   GETPALETTE
61         .word   GETDEFPALETTE
62         .word   SETPIXEL
63         .word   GETPIXEL
64         .word   LINE
65         .word   BAR
66         .word   CIRCLE
67         .word   TEXTSTYLE
68         .word   OUTTEXT
69
70 ; ------------------------------------------------------------------------
71 ; Data.
72
73 ; Variables mapped to the zero page segment variables. Some of these are
74 ; used for passing parameters to the driver.
75
76 X1              = ptr1
77 Y1              = ptr2
78 X2              = ptr3
79 Y2              = ptr4
80 RADIUS          = tmp1
81
82 ADDR            = tmp1
83 TEMP            = tmp3
84 TEMP2           = tmp4
85 TEMP3           = sreg
86 TEMP4           = sreg+1
87
88 ; Line routine stuff (must be on zpage)
89 PB              = ptr3          ; (2)   LINE
90 UB              = ptr4          ; (2)   LINE
91 ERR             = regsave       ; (2)   LINE
92 NX              = regsave+2     ; (2)   LINE
93 ; Circle stuff
94 XX              = ptr3          ; (2)   CIRCLE
95 YY              = ptr4          ; (2)   CIRCLE
96 MaxO            = sreg          ; (overwritten by TEMP3+TEMP4, but restored from OG/OU anyway)
97 XS              = regsave       ; (2)   CIRCLE
98 YS              = regsave+2     ; (2)   CIRCLE
99
100 ; Absolute variables used in the code
101
102 .bss
103
104 ERROR:          .res    1       ; Error code
105
106 ; Line routine stuff (combined with CIRCLE to save space)
107
108 OGora:
109 COUNT:          .res    2
110 OUkos:
111 NY:             .res    2
112 Y3:
113 DX:             .res    1
114 DY:             .res    1
115 AY:             .res    1
116
117 ; Constants and tables
118
119 .rodata
120
121 DEFPALETTE:     .byte   $0, $f, $1, $e, $3, $4, $2, $d
122                 .byte   $9, $8, $b, $5, $a, $c, $6, $7
123
124 .code
125
126 ; ------------------------------------------------------------------------
127 ; INSTALL routine. Is called after the driver is loaded into memory. May
128 ; initialize anything that has to be done just once. Is probably empty
129 ; most of the time.
130 ;
131 ; Must set an error code: NO
132 ;
133
134 INSTALL:
135
136
137 ; ------------------------------------------------------------------------
138 ; UNINSTALL routine. Is called before the driver is removed from memory. May
139 ; clean up anything done by INSTALL but is probably empty most of the time.
140 ;
141 ; Must set an error code: NO
142 ;
143
144 UNINSTALL:
145         rts
146
147
148 ; ------------------------------------------------------------------------
149 ; INIT: Changes an already installed device from text mode to graphics
150 ; mode.
151 ; Note that INIT/DONE may be called multiple times while the driver
152 ; is loaded, while INSTALL is only called once, so any code that is needed
153 ; to initializes variables and so on must go here. Setting palette and
154 ; clearing the screen is not needed because this is called by the graphics
155 ; kernel later.
156 ; The graphics kernel will never call INIT when a graphics mode is already
157 ; active, so there is no need to protect against that.
158 ;
159 ; Must set an error code: YES
160 ;
161
162 INIT:
163
164 ; Switch into graphics mode
165
166         jsr     SETGR
167
168 ; Done, reset the error code
169
170         lda     #TGI_ERR_OK
171         sta     ERROR
172         rts
173
174 ; ------------------------------------------------------------------------
175 ; DONE: Will be called to switch the graphics device back into text mode.
176 ; The graphics kernel will never call DONE when no graphics mode is active,
177 ; so there is no need to protect against that.
178 ;
179 ; Must set an error code: NO
180 ;
181
182 DONE            = TEXT
183
184 ; ------------------------------------------------------------------------
185 ; GETERROR: Return the error code in A and clear it.
186
187 GETERROR:
188         ldx     #TGI_ERR_OK
189         lda     ERROR
190         stx     ERROR
191         rts
192
193 ; ------------------------------------------------------------------------
194 ; CONTROL: Platform/driver specific entry point.
195 ;
196 ; Must set an error code: YES
197 ;
198
199 CONTROL:
200         lda     #TGI_ERR_INV_FUNC
201         sta     ERROR
202         rts
203
204 ; ------------------------------------------------------------------------
205 ; CLEAR: Clears the screen.
206 ;
207 ; Must set an error code: NO
208 ;
209
210 CLEAR           = CLRTOP
211
212 ; ------------------------------------------------------------------------
213 ; SETVIEWPAGE: Set the visible page. Called with the new page in A (0..n).
214 ; The page number is already checked to be valid by the graphics kernel.
215 ;
216 ; Must set an error code: NO (will only be called if page ok)
217 ;
218
219 SETVIEWPAGE:
220
221 ; ------------------------------------------------------------------------
222 ; SETDRAWPAGE: Set the drawable page. Called with the new page in A (0..n).
223 ; The page number is already checked to be valid by the graphics kernel.
224 ;
225 ; Must set an error code: NO (will only be called if page ok)
226 ;
227
228 SETDRAWPAGE:
229         rts
230
231 ; ------------------------------------------------------------------------
232 ; SETCOLOR: Set the drawing color (in A). The new color is already checked
233 ; to be in a valid range (0..maxcolor-1).
234 ;
235 ; Must set an error code: NO (will only be called if color ok)
236 ;
237
238 SETCOLOR        = SETCOL
239
240 ; ------------------------------------------------------------------------
241 ; SETPALETTE: Set the palette (not available with all drivers/hardware).
242 ; A pointer to the palette is passed in ptr1. Must set an error if palettes
243 ; are not supported
244 ;
245 ; Must set an error code: YES
246 ;
247
248 SETPALETTE:
249         lda     #TGI_ERR_INV_FUNC
250         sta     ERROR
251         rts
252
253 ; ------------------------------------------------------------------------
254 ; GETPALETTE: Return the current palette in A/X. Even drivers that cannot
255 ; set the palette should return the default palette here, so there's no
256 ; way for this function to fail.
257 ;
258 ; Must set an error code: NO
259 ;
260
261 GETPALETTE:
262
263 ; ------------------------------------------------------------------------
264 ; GETDEFPALETTE: Return the default palette for the driver in A/X. All
265 ; drivers should return something reasonable here, even drivers that don't
266 ; support palettes, otherwise the caller has no way to determine the colors
267 ; of the (not changeable) palette.
268 ;
269 ; Must set an error code: NO (all drivers must have a default palette)
270 ;
271
272 GETDEFPALETTE:
273         lda     #<DEFPALETTE
274         ldx     #>DEFPALETTE
275         rts
276
277 ; ------------------------------------------------------------------------
278 ; SETPIXEL: Draw one pixel at X1/Y1 = ptr1/ptr2 with the current drawing
279 ; color. The coordinates passed to this function are never outside the
280 ; visible screen area, so there is no need for clipping inside this function.
281 ;
282 ; Must set an error code: NO
283 ;
284
285 SETPIXELCLIP:
286         lda     Y1+1
287         bmi     @finito         ; y<0
288         lda     X1+1
289         bmi     @finito         ; x<0
290         lda     X1
291         ldx     X1+1
292         sta     ADDR
293         stx     ADDR+1
294         ldx     #ADDR
295         lda     xres
296         ldy     xres+1
297         jsr     icmp            ; ( x < xres ) ...
298         bcs     @finito
299         lda     Y1
300         ldx     Y1+1
301         sta     ADDR
302         stx     ADDR+1
303         ldx     #ADDR
304         lda     yres
305         ldy     yres+1
306         jsr     icmp            ; ... && ( y < yres )
307         bcc     SETPIXEL
308 @finito:rts
309
310 SETPIXEL:
311         lda     X1
312         ldy     Y1
313         jmp     PLOT
314
315 ; ------------------------------------------------------------------------
316 ; GETPIXEL: Read the color value of a pixel and return it in A/X. The
317 ; coordinates passed to this function are never outside the visible screen
318 ; area, so there is no need for clipping inside this function.
319
320
321 GETPIXEL:
322         lda     X1
323         ldy     Y1
324         jsr     SCRN
325         ldx     #0
326         rts
327
328 ; ------------------------------------------------------------------------
329 ; LINE: Draw a line from X1/Y1 to X2/Y2, where X1/Y1 = ptr1/ptr2 and
330 ; X2/Y2 = ptr3/ptr4 using the current drawing color.
331 ;
332 ; Must set an error code: NO
333 ;
334
335 LINE:
336         ; nx = abs(x2 - x1)
337         lda     X2
338         sub     X1
339         sta     NX
340         lda     X2+1
341         sbc     X1+1
342         tay
343         lda     NX
344         jsr     abs
345         sta     NX
346         sty     NX+1
347         ; ny = abs(y2 - y1)
348         lda     Y2
349         sub     Y1
350         sta     NY
351         lda     Y2+1
352         sbc     Y1+1
353         tay
354         lda     NY
355         jsr     abs
356         sta     NY
357         sty     NY+1
358         ; if (x2>x1)
359         ldx     #X2
360         lda     X1
361         ldy     X1+1
362         jsr     icmp
363         bcc     @L0243
364         beq     @L0243
365         ; dx = 1;
366         lda     #1
367         bne     @L0244
368         ; else
369         ; dx = -1;
370 @L0243: lda     #$ff
371 @L0244: sta     DX
372         ; if (y2>y1)
373         ldx     #Y2
374         lda     Y1
375         ldy     Y1+1
376         jsr     icmp
377         bcc     @L024A
378         beq     @L024A
379         ; dy = 1;
380         lda     #1
381         bne     @L024B
382         ; else
383         ; dy = -1;
384 @L024A: lda     #$ff
385 @L024B: sta     DY
386         ; err = ay = 0;
387         lda     #0
388         sta     ERR
389         sta     ERR+1
390         sta     AY
391
392         ; if (nx<ny) {
393         ldx     #NX
394         lda     NY
395         ldy     NY+1
396         jsr     icmp
397         bcs     @L0255
398         ;  nx <-> ny
399         lda     NX
400         ldx     NY
401         sta     NY
402         stx     NX
403         lda     NX+1
404         ldx     NY+1
405         sta     NY+1
406         stx     NX+1
407         ; ay = dx
408         lda     DX
409         sta     AY
410         ; dx = dy = 0;
411         lda     #0
412         sta     DX
413         sta     DY
414         ; ny = - ny;
415 @L0255: lda     NY
416         ldy     NY+1
417         jsr     neg
418         sta     NY
419         sty     NY+1
420         ; for (count=nx;count>0;--count) {
421         lda     NX
422         ldx     NX+1
423         sta     COUNT
424         stx     COUNT+1
425 @L0166: lda     COUNT           ; count>0
426         ora     COUNT+1
427         bne     @L0167
428         rts
429         ;    setpixel(X1,Y1)
430 @L0167: jsr     SETPIXELCLIP
431         ;    pb = err + ny
432         lda     ERR
433         add     NY
434         sta     PB
435         lda     ERR+1
436         adc     NY+1
437         sta     PB+1
438         tax
439         ;    ub = pb + nx
440         lda     PB
441         add     NX
442         sta     UB
443         txa
444         adc     NX+1
445         sta     UB+1
446         ;    x1 = x1 + dx
447         ldx     #0
448         lda     DX
449         bpl     @L027B
450         dex
451 @L027B: add     X1
452         sta     X1
453         txa
454         adc     X1+1
455         sta     X1+1
456         ;   y1 = y1 + ay
457         ldx     #0
458         lda     AY
459         bpl     @L027E
460         dex
461 @L027E: add     Y1
462         sta     Y1
463         txa
464         adc     Y1+1
465         sta     Y1+1
466         ; if (abs(pb)<abs(ub)) {
467         lda     PB
468         ldy     PB+1
469         jsr     abs
470         sta     TEMP3
471         sty     TEMP4
472         lda     UB
473         ldy     UB+1
474         jsr     abs
475         ldx     #TEMP3
476         jsr     icmp
477         bpl     @L027F
478         ;   err = pb
479         lda     PB
480         ldx     PB+1
481         jmp     @L0312
482         ; } else { x1 = x1 + ay
483 @L027F:
484         ldx     #0
485         lda     AY
486         bpl     @L0288
487         dex
488 @L0288: add     X1
489         sta     X1
490         txa
491         adc     X1+1
492         sta     X1+1
493         ;       y1 = y1 + dy
494         ldx     #0
495         lda     DY
496         bpl     @L028B
497         dex
498 @L028B: add     Y1
499         sta     Y1
500         txa
501         adc     Y1+1
502         sta     Y1+1
503         ;       err = ub }
504         lda     UB
505         ldx     UB+1
506 @L0312:
507         sta     ERR
508         stx     ERR+1
509         ; } (--count)
510         sec
511         lda     COUNT
512         sbc     #1
513         sta     COUNT
514         bcc     @L0260
515         jmp     @L0166
516 @L0260: dec     COUNT+1
517         jmp     @L0166
518
519 ; ------------------------------------------------------------------------
520 ; BAR: Draw a filled rectangle with the corners X1/Y1, X2/Y2, where
521 ; X1/Y1 = ptr1/ptr2 and X2/Y2 = ptr3/ptr4 using the current drawing color.
522 ; Contrary to most other functions, the graphics kernel will sort and clip
523 ; the coordinates before calling the driver, so on entry the following
524 ; conditions are valid:
525 ;       X1 <= X2
526 ;       Y1 <= Y2
527 ;       (X1 >= 0) && (X1 < XRES)
528 ;       (X2 >= 0) && (X2 < XRES)
529 ;       (Y1 >= 0) && (Y1 < YRES)
530 ;       (Y2 >= 0) && (Y2 < YRES)
531 ;
532 ; Must set an error code: NO
533 ;
534
535 BAR:
536         inc     Y2
537         ldx     X2
538         stx     H2
539 @L1:    ldy     X1
540         lda     Y1
541         jsr     HLINE
542         inc     Y1
543         lda     Y2
544         cmp     Y1
545         bne     @L1
546         rts
547
548 ; ------------------------------------------------------------------------
549 ; CIRCLE: Draw a circle around the center X1/Y1 (= ptr1/ptr2) with the
550 ; radius in tmp1 and the current drawing color.
551 ;
552 ; Must set an error code: NO
553 ;
554
555 CIRCLE:
556         lda     RADIUS
557         bne     @L1
558         jmp     SETPIXELCLIP    ; Plot as a point
559
560 @L1:    sta     XX
561         ; x = r;
562         lda     #0
563         sta     XX+1
564         sta     YY
565         sta     YY+1
566         sta     MaxO
567         sta     MaxO+1
568         ; y =0; mo=0;
569         lda     X1
570         ldx     X1+1
571         sta     XS
572         stx     XS+1
573         lda     Y1
574         ldx     Y1+1
575         sta     YS
576         stx     YS+1            ; XS/YS to remember the center
577
578         ; while (y<x) {
579 @L013B: ldx     #YY
580         lda     XX
581         ldy     XX+1
582         jsr     icmp
583         bcc     @L12
584         rts
585 @L12:   ; plot points in 8 slices...
586         lda     XS
587         add     XX
588         sta     X1
589         lda     XS+1
590         adc     XX+1
591         sta     X1+1            ; x1 = xs+x
592         lda     YS
593         add     YY
594         sta     Y1
595         pha
596         lda     YS+1
597         adc     YY+1
598         sta     Y1+1            ; (stack)=ys+y, y1=(stack)
599         pha
600         jsr     SETPIXELCLIP    ; plot(xs+x,ys+y)
601         lda     YS
602         sub     YY
603         sta     Y1
604         sta     Y3
605         lda     YS+1
606         sbc     YY+1
607         sta     Y1+1            ; y3 = y1 = ys-y
608         sta     Y3+1
609         jsr     SETPIXELCLIP    ; plot(xs+x,ys-y)
610         pla
611         sta     Y1+1
612         pla
613         sta     Y1              ; y1 = ys+y
614         lda     XS
615         sub     XX
616         sta     X1
617         lda     XS+1
618         sbc     XX+1
619         sta     X1+1
620         jsr     SETPIXELCLIP    ; plot (xs-x,ys+y)
621         lda     Y3
622         sta     Y1
623         lda     Y3+1
624         sta     Y1+1
625         jsr     SETPIXELCLIP    ; plot (xs-x,ys-y)
626
627         lda     XS
628         add     YY
629         sta     X1
630         lda     XS+1
631         adc     YY+1
632         sta     X1+1            ; x1 = xs+y
633         lda     YS
634         add     XX
635         sta     Y1
636         pha
637         lda     YS+1
638         adc     XX+1
639         sta     Y1+1            ; (stack)=ys+x, y1=(stack)
640         pha
641         jsr     SETPIXELCLIP    ; plot(xs+y,ys+x)
642         lda     YS
643         sub     XX
644         sta     Y1
645         sta     Y3
646         lda     YS+1
647         sbc     XX+1
648         sta     Y1+1            ; y3 = y1 = ys-x
649         sta     Y3+1
650         jsr     SETPIXELCLIP    ; plot(xs+y,ys-x)
651         pla
652         sta     Y1+1
653         pla
654         sta     Y1              ; y1 = ys+x(stack)
655         lda     XS
656         sub     YY
657         sta     X1
658         lda     XS+1
659         sbc     YY+1
660         sta     X1+1
661         jsr     SETPIXELCLIP    ; plot (xs-y,ys+x)
662         lda     Y3
663         sta     Y1
664         lda     Y3+1
665         sta     Y1+1
666         jsr     SETPIXELCLIP    ; plot (xs-y,ys-x)
667
668         ; og = mo+y+y+1
669         lda     MaxO
670         ldx     MaxO+1
671         add     YY
672         tay
673         txa
674         adc     YY+1
675         tax
676         tya
677         add     YY
678         tay
679         txa
680         adc     YY+1
681         tax
682         tya
683         add     #1
684         bcc     @L0143
685         inx
686 @L0143: sta     OGora
687         stx     OGora+1
688         ; ou = og-x-x+1
689         sub     XX
690         tay
691         txa
692         sbc     XX+1
693         tax
694         tya
695         sub     XX
696         tay
697         txa
698         sbc     XX+1
699         tax
700         tya
701         add     #1
702         bcc     @L0146
703         inx
704 @L0146: sta     OUkos
705         stx     OUkos+1
706         ; ++y
707         inc     YY
708         bne     @L0148
709         inc     YY+1
710 @L0148: ; if (abs(ou)<abs(og))
711         lda     OUkos
712         ldy     OUkos+1
713         jsr     abs
714         sta     TEMP3
715         sty     TEMP4
716         lda     OGora
717         ldy     OGora+1
718         jsr     abs
719         ldx     #TEMP3
720         jsr     icmp
721         bpl     @L0149
722         ; { --x;
723         lda     XX
724         sub     #1
725         sta     XX
726         bcs     @L014E
727         dec     XX+1
728 @L014E: ; mo = ou; }
729         lda     OUkos
730         ldx     OUkos+1
731         jmp     @L014G
732         ; else { mo = og }
733 @L0149: lda     OGora
734         ldx     OGora+1
735 @L014G: sta     MaxO
736         stx     MaxO+1
737         ; }
738         jmp     @L013B
739
740 ; ------------------------------------------------------------------------
741 ; TEXTSTYLE: Set the style used when calling OUTTEXT. Text scaling in X and Y
742 ; direction is passend in X/Y, the text direction is passed in A.
743 ;
744 ; Must set an error code: NO
745 ;
746
747 TEXTSTYLE:
748         rts
749
750
751 ; ------------------------------------------------------------------------
752 ; OUTTEXT: Output text at X/Y = ptr1/ptr2 using the current color and the
753 ; current text style. The text to output is given as a zero terminated
754 ; string with address in ptr3.
755 ;
756 ; Must set an error code: NO
757 ;
758
759 OUTTEXT:
760         lda     ptr1
761         sta     CH
762         lda     ptr2
763         jsr     VTABZ
764         ldy     #0
765 @L1:    lda     (ptr3),y
766         ora     #$80
767         jsr     COUT
768         iny
769         cmp     #$80
770         bne     @L1
771         rts
772
773 ;-------------
774 ; copies of some runtime routines
775
776 abs:
777         ; a/y := abs(a/y)
778         cpy     #0
779         bpl     absend
780         ; negay
781 neg:    clc
782         eor     #$ff
783         adc     #1
784         pha
785         tya
786         eor     #$ff
787         adc     #0
788         tay
789         pla
790 absend: rts
791
792 icmp:
793         ; compare a/y to zp,x
794         sta     TEMP            ; TEMP/TEMP2 - arg2
795         sty     TEMP2
796         lda     $0,x
797         pha
798         lda     $1,x
799         tay
800         pla
801         tax
802         tya                     ; x/a - arg1 (a=high)
803
804         sub     TEMP2
805         bne     @L4
806         cpx     TEMP
807         beq     @L3
808         adc     #$ff
809         ora     #$01
810 @L3:    rts
811 @L4:    bvc     @L3
812         eor     #$ff
813         ora     #$01
814         rts