]> git.sur5r.net Git - cc65/blob - libsrc/c128/c128-640-200-2.s
26ab0b9d16ad4f0dc44f85c3e4db7077b4901cd5
[cc65] / libsrc / c128 / c128-640-200-2.s
1 ;
2 ; Graphics driver for the 640x200x2 mode on the C128 VDC
3 ; Maciej 'YTM/Elysium' Witkowiak <ytm@elysium.pl>
4 ; 23.12.2002
5 ;
6 ; NOTES:
7 ; On the real machine there are some additional pixels appearing (probably bad reads)
8 ; VICE emulator does not show them. Needs further investigation.
9 ;
10 ; Only DONE routine contains C128-mode specific stuff, everything else will work in
11 ; C64-mode of C128 (C64 needs full VDC init then).
12 ;
13 ; X-axis resolution can be changed by manipulating xres header field so
14 ; any resolution 8..708 is possible only with gfx-mode initialization and CALC changed.
15 ;
16 ; With special initialization and CALC we can get 320x200 double-pixel mode.
17 ;
18 ; Color translation values for BROWN and GRAY3 are obviously wrong, they could
19 ; be replaced by equiv. of ORANGE and GRAY2 but this would give only 14 of 16 colors available.
20 ;
21 ; Register 25 ($19) is said to require different value for VDC v1, but I couldn't find what
22 ; it should be.
23
24         .include        "zeropage.inc"
25
26         .include        "tgi-kernel.inc"
27         .include        "tgi-mode.inc"
28         .include        "tgi-error.inc"
29
30
31         .macpack        generic
32
33 ; ------------------------------------------------------------------------
34 ; Constants
35
36 VDC_ADDR_REG      = $D600                 ; VDC address
37 VDC_DATA_REG      = $D601                 ; VDC data
38
39 VDC_DSP_HI        = 12                    ; registers used
40 VDC_DSP_LO        = 13
41 VDC_DATA_HI       = 18
42 VDC_DATA_LO       = 19
43 VDC_VSCROLL       = 24
44 VDC_HSCROLL       = 25
45 VDC_COLORS        = 26
46 VDC_CSET          = 28
47 VDC_COUNT         = 30
48 VDC_DATA          = 31
49
50 ; ------------------------------------------------------------------------
51 ; Header. Includes jump table and constants.
52
53 .segment        "JUMPTABLE"
54
55 ; First part of the header is a structure that has a magic and defines the
56 ; capabilities of the driver
57
58         .byte   $74, $67, $69           ; "tgi"
59         .byte   $00                     ; TGI version number
60 xres:   .word   640                     ; X resolution
61 yres:   .word   200                     ; Y resolution
62         .byte   2                       ; Number of drawing colors
63 pages:  .byte   1                       ; Number of screens available
64         .byte   8                       ; System font X size
65         .byte   8                       ; System font Y size
66         .res    4, $00                  ; Reserved for future extensions
67
68 ; Next comes the jump table. Currently all entries must be valid and may point
69 ; to an RTS for test versions (function not implemented). A future version may
70 ; allow for emulation: In this case the vector will be zero. Emulation means
71 ; that the graphics kernel will emulate the function by using lower level
72 ; primitives - for example ploting a line by using calls to SETPIXEL.
73
74         .word   INSTALL
75         .word   DEINSTALL
76         .word   INIT
77         .word   DONE
78         .word   GETERROR
79         .word   CONTROL
80         .word   CLEAR
81         .word   SETVIEWPAGE
82         .word   SETDRAWPAGE
83         .word   SETCOLOR
84         .word   SETPALETTE
85         .word   GETPALETTE
86         .word   GETDEFPALETTE
87         .word   SETPIXEL
88         .word   GETPIXEL
89         .word   HORLINE
90         .word   LINE
91         .word   BAR
92         .word   CIRCLE
93         .word   TEXTSTYLE
94         .word   OUTTEXT
95
96 ; ------------------------------------------------------------------------
97 ; Data.
98
99 ; Variables mapped to the zero page segment variables. Some of these are
100 ; used for passing parameters to the driver.
101
102 X1              = ptr1
103 Y1              = ptr2
104 X2              = ptr3
105 Y2              = ptr4
106 RADIUS          = tmp1
107
108 ADDR            = tmp1          ; (2)   CALC
109 TEMP            = tmp3          ;       CALC icmp
110 TEMP2           = tmp4          ;       icmp
111 TEMP3           = sreg          ;       LINE
112 TEMP4           = sreg+1        ;       LINE
113
114 ; Line routine stuff (must be on zpage)
115 PB              = ptr3          ; (2)   LINE
116 UB              = ptr4          ; (2)   LINE
117 ERR             = regsave       ; (2)   LINE
118 NX              = regsave+2     ; (2)   LINE
119 ; Circle stuff
120 XX              = ptr3          ; (2)   CIRCLE
121 YY              = ptr4          ; (2)   CIRCLE
122 MaxO            = sreg          ; (overwritten by TEMP3+TEMP4, but restored from OG/OU anyway)
123 XS              = regsave       ; (2)   CIRCLE
124 YS              = regsave+2     ; (2)   CIRCLE
125
126 ; Absolute variables used in the code
127
128 .bss
129
130 SCRBASE:        .res    1       ; High byte of screen base
131
132 ERROR:          .res    1       ; Error code
133 PALETTE:        .res    2       ; The current palette
134
135 BITMASK:        .res    1       ; $00 = clear, $FF = set pixels
136
137 OLDCOLOR:       .res    1       ; colors before entering gfx mode
138
139 ; Line routine stuff (combined with CIRCLE to save space)
140
141 OGora:
142 COUNT:           .res   2
143 OUkos:
144 NY:              .res   2
145 Y3:
146 DX:              .res   1
147 DY:              .res   1
148 AY:              .res   1
149
150 ; Text output stuff
151 TEXTMAGX:       .res    1
152 TEXTMAGY:       .res    1
153 TEXTDIR:        .res    1
154
155 ; Constants and tables
156
157 .rodata
158
159 DEFPALETTE:     .byte   $00, $0f        ; White on black
160 PALETTESIZE     = * - DEFPALETTE
161
162 BITTAB:         .byte   $80,$40,$20,$10,$08,$04,$02,$01
163
164 BITMASKL:       .byte   %11111111, %01111111, %00111111, %00011111
165                 .byte   %00001111, %00000111, %00000011, %00000001
166
167 BITMASKR:       .byte   %10000000, %11000000, %11100000, %11110000
168                 .byte   %11111000, %11111100, %11111110, %11111111
169
170 ; color translation table (indexed by VIC color)
171 COLTRANS:       .byte $00, $0f, $08, $06, $0a, $04, $02, $0c
172                 .byte $0d, $0b, $09, $01, $0e, $05, $03, $07
173                 ; colors BROWN and GRAY3 are wrong
174
175 ; VDC initialization table (reg),(val),...,$ff
176 InitVDCTab:
177                 .byte VDC_DSP_HI, 0             ; viewpage 0 as default
178                 .byte VDC_DSP_LO, 0
179                 .byte VDC_HSCROLL, $87
180                 .byte $ff
181
182 SCN80CLR:       .byte 27,88,147,27,88,0
183
184 .code
185
186 ; ------------------------------------------------------------------------
187 ; INSTALL routine. Is called after the driver is loaded into memory. May
188 ; initialize anything that has to be done just once. Is probably empty
189 ; most of the time.
190 ;
191 ; Must set an error code: NO
192 ;
193
194 INSTALL:
195         ; check for VDC version and update register $19 value
196
197         ; check for VDC ram size and update number of available screens
198
199         ldx     #VDC_CSET       ; determine size of RAM...
200         jsr     VDCReadReg
201         sta     tmp1
202         ora     #%00010000
203         jsr     VDCWriteReg     ; turn on 64k
204
205         jsr     settestadr1     ; save original value of test byte
206         jsr     VDCReadByte
207         sta     tmp2
208
209         lda     #$55            ; write $55 here
210         ldy     #ptr1
211         jsr     test64k         ; read it here and there
212         lda     #$aa            ; write $aa here
213         ldy     #ptr2
214         jsr     test64k         ; read it here and there
215
216         jsr     settestadr1
217         lda     tmp2
218         jsr     VDCWriteByte    ; restore original value of test byte
219
220         lda     ptr1            ; do bytes match?
221         cmp     ptr1+1
222         bne     @have64k
223         lda     ptr2
224         cmp     ptr2+1
225         bne     @have64k
226
227         ldx     #VDC_CSET
228         lda     tmp1
229         jsr     VDCWriteReg     ; restore 16/64k flag
230         jmp     @endok          ; and leave default values for 16k
231
232 @have64k:
233         lda     #4
234         sta     pages
235 @endok:
236         lda     #0
237         sta     SCRBASE         ; draw page 0 as default
238         rts
239
240 test64k:
241         sta     tmp1
242         sty     ptr3
243         lda     #0
244         sta     ptr3+1
245         jsr     settestadr1
246         lda     tmp1
247         jsr     VDCWriteByte            ; write $55
248         jsr     settestadr1
249         jsr     VDCReadByte             ; read here
250         pha
251         jsr     settestadr2
252         jsr     VDCReadByte             ; and there
253         ldy     #1
254         sta     (ptr3),y
255         pla
256         dey
257         sta     (ptr3),y
258         rts
259
260 settestadr1:
261         ldy     #$02                    ; test page 2 (here)
262         .byte   $2c
263 settestadr2:
264         ldy     #$42                    ; or page 64+2 (there)
265         lda     #0
266         jmp     VDCSetSourceAddr
267
268 ; ------------------------------------------------------------------------
269 ; DEINSTALL routine. Is called before the driver is removed from memory. May
270 ; clean up anything done by INSTALL but is probably empty most of the time.
271 ;
272 ; Must set an error code: NO
273 ;
274
275 DEINSTALL:
276         rts
277
278
279 ; ------------------------------------------------------------------------
280 ; INIT: Changes an already installed device from text mode to graphics
281 ; mode. The number of the graphics mode is passed to the function in A.
282 ; Note that INIT/DONE may be called multiple times while the driver
283 ; is loaded, while INSTALL is only called once, so any code that is needed
284 ; to initializes variables and so on must go here. Setting palette and
285 ; clearing the screen is not needed because this is called by the graphics
286 ; kernel later.
287 ; The graphics kernel will never call INIT when a graphics mode is already
288 ; active, so there is no need to protect against that.
289 ;
290 ; Must set an error code: YES
291 ;
292
293 INIT:   cmp     #TGI_MODE_640_200_2     ; Correct mode?
294         beq     @L1                     ; Jump if yes
295         lda     #TGI_ERR_INV_MODE       ; ## Error
296         bne     @L9
297
298 ; Initialize variables
299
300 @L1:    ldx     #$FF
301         stx     BITMASK
302
303 ; Switch into graphics mode (set view page 0)
304
305         ldy     #0
306 @L2:    ldx     InitVDCTab,y
307         bmi     @L3
308         iny
309         lda     InitVDCTab,y
310         jsr     VDCWriteReg
311         iny
312         bne     @L2
313 @L3:
314
315 ; Remeber current color value
316         ldx     #VDC_COLORS
317         jsr     VDCReadReg
318         sta     OLDCOLOR
319
320 ; Done, reset the error code
321
322         lda     #TGI_ERR_OK
323 @L9:    sta     ERROR
324         rts
325
326 ; ------------------------------------------------------------------------
327 ; DONE: Will be called to switch the graphics device back into text mode.
328 ; The graphics kernel will never call DONE when no graphics mode is active,
329 ; so there is no need to protect against that.
330 ;
331 ; Must set an error code: YES
332 ;
333
334 DONE:
335         ; This part is C128-mode specific
336         jsr $ce0c               ; reload character set
337         lda $d7                 ; in 80-columns?
338         bne @L01
339 @L0:    lda SCN80CLR,y
340         beq @L1
341         jsr $ffd2               ; print \xe,clr,\xe
342         iny
343         bne @L0
344 @L01:   lda #147
345         jsr $ffd2               ; print clr
346 @L1:    lda #0                  ; restore view page
347         ldx #VDC_DSP_HI
348         jsr VDCWriteReg
349         lda OLDCOLOR
350         ldx #VDC_COLORS
351         jsr VDCWriteReg         ; restore color (background)
352         lda #$47
353         ldx #VDC_HSCROLL
354         jmp VDCWriteReg         ; switch to text screen
355
356 ; ------------------------------------------------------------------------
357 ; GETERROR: Return the error code in A and clear it.
358
359 GETERROR:
360         ldx     #TGI_ERR_OK
361         lda     ERROR
362         stx     ERROR
363         rts
364
365 ; ------------------------------------------------------------------------
366 ; CONTROL: Platform/driver specific entry point.
367 ;
368 ; Must set an error code: YES
369 ;
370
371 CONTROL:
372         lda     #TGI_ERR_INV_FUNC
373         sta     ERROR
374         rts
375
376 ; ------------------------------------------------------------------------
377 ; CLEAR: Clears the screen.
378 ;
379 ; Must set an error code: NO
380 ;
381
382 CLEAR:  
383         lda     #0
384         ldy     SCRBASE
385         jsr     VDCSetSourceAddr
386         lda     #0
387         ldx     #VDC_VSCROLL
388         jsr     VDCWriteReg                     ; set fill mode
389         lda     #0
390         jsr     VDCWriteByte                    ; put 1rst byte (fill value)
391         ldy     #62                             ; 62 times
392         lda     #0                              ; 256 bytes
393         ldx     #VDC_COUNT
394 @L1:    jsr     VDCWriteReg
395         dey
396         bne     @L1
397         lda     #127
398         jmp     VDCWriteReg                     ; 1+62*256+127=16000=(640*256)/8
399
400 ; ------------------------------------------------------------------------
401 ; SETVIEWPAGE: Set the visible page. Called with the new page in A (0..n).
402 ; The page number is already checked to be valid by the graphics kernel.
403 ;
404 ; Must set an error code: NO (will only be called if page ok)
405 ;
406
407 SETVIEWPAGE:
408         clc
409         ror
410         ror
411         ror
412         ldx     #VDC_DSP_HI
413         jmp     VDCWriteReg
414
415 ; ------------------------------------------------------------------------
416 ; SETDRAWPAGE: Set the drawable page. Called with the new page in A (0..n).
417 ; The page number is already checked to be valid by the graphics kernel.
418 ;
419 ; Must set an error code: NO (will only be called if page ok)
420 ;
421
422 SETDRAWPAGE:
423         clc
424         ror
425         ror
426         ror
427         sta     SCRBASE
428         rts
429
430 ; ------------------------------------------------------------------------
431 ; SETCOLOR: Set the drawing color (in A). The new color is already checked
432 ; to be in a valid range (0..maxcolor-1).
433 ;
434 ; Must set an error code: NO (will only be called if color ok)
435 ;
436
437 SETCOLOR:
438         tax
439         beq     @L1
440         lda     #$FF
441 @L1:    sta     BITMASK
442         rts
443
444 ; ------------------------------------------------------------------------
445 ; SETPALETTE: Set the palette (not available with all drivers/hardware).
446 ; A pointer to the palette is passed in ptr1. Must set an error if palettes
447 ; are not supported
448 ;
449 ; Must set an error code: YES
450 ;
451
452 SETPALETTE:
453         ldy     #PALETTESIZE - 1
454 @L1:    lda     (ptr1),y        ; Copy the palette
455         and     #$0F            ; Make a valid color
456         sta     PALETTE,y
457         dey
458         bpl     @L1
459
460 ; Get the color entries from the palette
461
462         ldy     PALETTE+1       ; Foreground color
463         lda     COLTRANS,y
464         asl     a
465         asl     a
466         asl     a
467         asl     a
468         ldy     PALETTE         ; Background color
469         ora     COLTRANS,y
470
471         ldx     #VDC_COLORS
472         jmp     VDCWriteReg
473
474 ; ------------------------------------------------------------------------
475 ; GETPALETTE: Return the current palette in A/X. Must return NULL and set an
476 ; error if palettes are not supported.
477 ;
478 ; Must set an error code: YES
479 ;
480
481 GETPALETTE:
482         lda     #<PALETTE
483         ldx     #>PALETTE
484         rts
485
486 ; ------------------------------------------------------------------------
487 ; GETDEFPALETTE: Return the default palette for the driver in A/X. Must
488 ; return NULL and set an error of palettes are not supported.
489 ;
490 ; Must set an error code: YES
491 ;
492
493 GETDEFPALETTE:
494         lda     #<DEFPALETTE
495         ldx     #>DEFPALETTE
496         rts
497
498 ; ------------------------------------------------------------------------
499 ; SETPIXEL: Draw one pixel at X1/Y1 = ptr1/ptr2 with the current drawing
500 ; color. The coordinates passed to this function are never outside the
501 ; visible screen area, so there is no need for clipping inside this function.
502 ;
503 ; Must set an error code: NO
504 ;
505
506 SETPIXELCLIP:
507         lda     Y1+1
508         bmi     @finito         ; y<0
509         lda     X1+1
510         bmi     @finito         ; x<0
511         lda     xres
512         ldx     xres+1
513         sta     ADDR
514         stx     ADDR+1
515         ldx     #ADDR
516         lda     X1
517         ldy     X1+1
518         jsr     icmp            ; if (xres<x1)
519         bcs     @cont           ; !(xres<x1)
520 @finito:rts
521 @cont:  lda     yres
522         ldx     yres+1
523         sta     ADDR
524         stx     ADDR+1
525         ldx     #ADDR
526         lda     Y1
527         ldy     Y1+1
528         jsr     icmp            ; if (yres<y1)
529         bcc     @finito
530
531 SETPIXEL:
532         jsr     CALC            ; Calculate coordinates
533
534         stx     TEMP
535         lda     ADDR
536         ldy     ADDR+1
537         jsr     VDCSetSourceAddr
538         jsr     VDCReadByte
539         ldx     TEMP
540
541         sta     TEMP
542         eor     BITMASK
543         and     BITTAB,X
544         eor     TEMP
545         pha
546         lda     ADDR
547         ldy     ADDR+1
548         jsr     VDCSetSourceAddr
549         pla
550         jsr     VDCWriteByte
551
552 @L9:    rts
553
554 ; ------------------------------------------------------------------------
555 ; GETPIXEL: Read the color value of a pixel and return it in A/X. The
556 ; coordinates passed to this function are never outside the visible screen
557 ; area, so there is no need for clipping inside this function.
558
559
560 GETPIXEL:
561         jsr     CALC            ; Calculate coordinates
562
563         stx     TEMP            ; preserve X
564         lda     ADDR
565         ldy     ADDR+1
566         jsr     VDCSetSourceAddr
567         jsr     VDCReadByte
568         ldx     TEMP
569
570         ldy     #$00
571         and     BITTAB,X
572         beq     @L1
573         iny
574
575 @L1:    tya                     ; Get color value into A
576         ldx     #$00            ; Clear high byte
577         rts
578
579 ; ------------------------------------------------------------------------
580 ; HORLINE: Draw a horizontal line from X1/Y to X2/Y, where X1 = ptr1,
581 ; Y = ptr2 and X2 = ptr3, using the current drawing color.
582 ;
583 ; This is a special line drawing entry used when the line is know to be
584 ; horizontal, for example by the BAR emulation routine. If the driver does
585 ; not have special code for horizontal lines, it may just copy Y to Y2 and
586 ; proceed with the generic line drawing code.
587 ;
588 ; Note: Line coordinates will always be sorted (Y1 <= X2) and clipped.
589 ;
590 ; Must set an error code: NO
591 ;
592
593 HORLINE:
594         lda X1
595         pha
596         lda X1+1
597         pha
598         jsr CALC                ; get data for LEFT
599         lda BITMASKL,x          ; remember left address and bitmask
600         pha
601         lda ADDR
602         pha
603         lda ADDR+1
604         pha
605
606         lda X2
607         sta X1
608         lda X2+1
609         sta X1+1
610         jsr CALC                ; get data for RIGHT
611         lda BITMASKR,x
612         sta TEMP3
613
614         pla                     ; recall data for LEFT
615         sta X1+1
616         pla
617         sta X1                  ; put left address into X1
618         pla
619
620         cmp #%11111111          ; if left bit <> 0
621         beq @L1
622         sta TEMP2               ; do left byte only...
623         lda X1
624         ldy X1+1
625         jsr VDCSetSourceAddr
626         jsr VDCReadByte
627         sta TEMP
628         eor BITMASK
629         and TEMP2
630         eor TEMP
631         pha
632         lda X1
633         ldy X1+1
634         jsr VDCSetSourceAddr
635         pla
636         jsr VDCWriteByte
637         inc X1                  ; ... and proceed
638         bne @L1
639         inc X1+1
640
641         ; do right byte (if Y2=0 ++ADDR and skip)
642 @L1:    lda TEMP3
643         cmp #%11111111          ; if right bit <> 7
644         bne @L11
645         inc ADDR                ; right bit = 7 - the next one is the last
646         bne @L10
647         inc ADDR+1
648 @L10:   bne @L2
649
650 @L11:   lda ADDR                ; do right byte only...
651         ldy ADDR+1
652         jsr VDCSetSourceAddr
653         jsr VDCReadByte
654         sta TEMP
655         eor BITMASK
656         and TEMP3
657         eor TEMP
658         pha
659         lda ADDR
660         ldy ADDR+1
661         jsr VDCSetSourceAddr
662         pla
663         jsr VDCWriteByte
664
665 @L2:                            ; do the fill in the middle
666         lda ADDR                ; calculate offset in full bytes
667         sec
668         sbc X1
669         beq @L3                 ; if equal - there are no more bytes
670         sta ADDR
671
672         lda X1                  ; setup for the left side
673         ldy X1+1
674         jsr VDCSetSourceAddr
675         lda BITMASK             ; get color
676         jsr VDCWriteByte        ; put 1st value
677         ldx ADDR
678         dex
679         beq @L3                 ; 1 byte already written
680
681         stx ADDR                ; if there are more bytes - fill them...
682         ldx #VDC_VSCROLL
683         lda #0
684         jsr VDCWriteReg         ; setup for fill
685         ldx #VDC_COUNT
686         lda ADDR
687         jsr VDCWriteReg         ; ... fill them NOW!
688
689 @L3:    pla
690         sta X1+1
691         pla
692         sta X1
693         rts
694
695 ; ------------------------------------------------------------------------
696 ; LINE: Draw a line from X1/Y1 to X2/Y2, where X1/Y1 = ptr1/ptr2 and
697 ; X2/Y2 = ptr3/ptr4 using the current drawing color.
698 ;
699 ; Must set an error code: NO
700 ;
701
702 LINE:
703         ; if (x2>x1) {
704         ldx     #X1
705         lda     X2
706         ldy     X2+1
707         jsr     icmp
708         bcc     @L0137
709         beq     @L0137
710         ;  x2<->x1 }
711         lda     X1
712         ldx     X2
713         sta     X2
714         stx     X1
715         lda     X1+1
716         ldx     X2+1
717         sta     X2+1
718         stx     X1+1
719 @L0137: ; if (y2>y1) {
720         ldx     #Y1
721         lda     Y2
722         ldy     Y2+1
723         jsr     icmp
724         bcc     @L013F
725         bne     @nequal
726         jmp     HORLINE         ; x1/x2 are sorted, y1==y2 - do faster horizontal line draw
727 @nequal:
728         ; y2<->y1 }
729         lda     Y1
730         ldx     Y2
731         sta     Y2
732         stx     Y1
733         lda     Y1+1
734         ldx     Y2+1
735         sta     Y2+1
736         stx     Y1+1
737 @L013F:
738         ; nx = x2 - x1
739         lda     X2
740         sec
741         sbc     X1
742         sta     NX
743         lda     X2+1
744         sbc     X1+1
745         sta     NX+1
746         ; ny = y2 - y1
747         lda     Y2
748         sec
749         sbc     Y1
750         sta     NY
751         lda     Y2+1
752         sbc     Y1+1
753         sta     NY+1
754         ; if (nx<ny) {
755         ldx     #NX
756         lda     NY
757         ldy     NY+1
758         jsr     icmp
759         bcs     @L041B
760         ;  nx <-> ny
761         lda     NX
762         ldx     NY
763         sta     NY
764         stx     NX
765         lda     NX+1
766         ldx     NY+1
767         sta     NY+1
768         stx     NX+1
769         ; dx = dy = 0; ax = ay = 1 }
770         ldy     #1
771         sty     AY
772         dey
773         beq     @L025A
774         ; else { dx = dy = 1; ax = ay = 0 }
775 @L041B: ldy     #0
776         sty     AY
777         iny
778 @L025A: sty     DX
779         sty     DY
780         ; err = 0
781         lda     #0
782         sta     ERR
783         sta     ERR+1
784         ; for (count=nx;count>0;--count) {
785         lda     NX
786         ldx     NX+1
787         sta     COUNT
788         stx     COUNT+1
789 @L0166: lda     COUNT           ; count>0
790         ora     COUNT+1
791         bne     @L0167
792         rts
793         ;    setpixel(X1,Y1)
794 @L0167: jsr     SETPIXELCLIP
795         ;    pb = err - ny
796         lda     ERR
797         sec
798         sbc     NY
799         sta     PB
800         lda     ERR+1
801         sbc     NY+1
802         sta     PB+1
803         tax
804         ;    ub = pb + nx
805         lda     PB
806         clc
807         adc     NX
808         sta     UB
809         txa
810         adc     NX+1
811         sta     UB+1
812         ;    x1 = x1 + dx
813         lda     X1
814         clc
815         adc     DX
816         sta     X1
817         bcc     @L0254
818         inc     X1+1
819         ;   y1 = y1 + ay
820 @L0254:
821         lda     Y1
822         clc
823         adc     AY
824         sta     Y1
825         bcc     @L0255
826         inc     Y1+1
827 @L0255:
828         ; if (abs(pb)<abs(ub)) {
829         lda     PB
830         ldy     PB+1
831         jsr     abs
832         sta     TEMP3
833         sty     TEMP4
834         lda     UB
835         ldy     UB+1
836         jsr     abs
837         ldx     #TEMP3
838         jsr     icmp
839         bpl     @L017B
840         ;   err = pb
841         lda     PB
842         ldx     PB+1
843         jmp     @L025B
844         ; } else { x1 = x1 + ay
845 @L017B: 
846         lda     X1
847         clc
848         adc     AY
849         sta     X1
850         bcc     @L0256
851         inc     X1+1
852         ;       y1 = y1 + dy
853 @L0256:
854         lda     Y1
855         clc
856         adc     DY
857         sta     Y1
858         bcc     @L0257
859         inc     Y1+1
860         ;       err = ub }
861 @L0257:
862         lda     UB
863         ldx     UB+1
864 @L025B:
865         sta     ERR
866         stx     ERR+1
867         ; } (--count)
868         sec
869         lda     COUNT
870         sbc     #1
871         sta     COUNT
872         bcc     @L0260
873         jmp     @L0166
874 @L0260: dec     COUNT+1
875         jmp     @L0166
876
877 ; ------------------------------------------------------------------------
878 ; BAR: Draw a filled rectangle with the corners X1/Y1, X2/Y2, where
879 ; X1/Y1 = ptr1/ptr2 and X2/Y2 = ptr3/ptr4 using the current drawing color.
880 ; Contrary to most other functions, the graphics kernel will sort and clip
881 ; the coordinates before calling the driver, so on entry the following
882 ; conditions are valid:
883 ;       X1 <= X2
884 ;       Y1 <= Y2
885 ;       (X1 >= 0) && (X1 < XRES)
886 ;       (X2 >= 0) && (X2 < XRES)
887 ;       (Y1 >= 0) && (Y1 < YRES)
888 ;       (Y2 >= 0) && (Y2 < YRES)
889 ;
890 ; Must set an error code: NO
891 ;
892
893 BAR:
894         inc     Y2
895         bne     @L0
896         inc     Y2+1
897 @L0:    jsr     HORLINE
898         inc     Y1
899         bne     @L1
900         inc     Y1+1
901 @L1:    lda     Y1
902         cmp     Y2
903         bne     @L0
904         lda     Y1+1
905         cmp     Y2+1
906         bne     @L0
907         rts
908
909 ; ------------------------------------------------------------------------
910 ; CIRCLE: Draw a circle around the center X1/Y1 (= ptr1/ptr2) with the
911 ; radius in tmp1 and the current drawing color.
912 ;
913 ; Must set an error code: NO
914 ;
915
916 CIRCLE: 
917         lda     RADIUS
918         bne     @L1
919         jmp     SETPIXELCLIP    ; Plot as a point
920
921 @L1:    sta     XX
922         ; x = r;
923         lda     #0
924         sta     XX+1
925         sta     YY
926         sta     YY+1
927         sta     MaxO
928         sta     MaxO+1
929         ; y =0; mo=0;
930         lda     X1
931         ldx     X1+1
932         sta     XS
933         stx     XS+1
934         lda     Y1
935         ldx     Y1+1
936         sta     YS
937         stx     YS+1            ; XS/YS to remember the center
938
939         ; while (y<x) {
940 @L013B: ldx     #YY
941         lda     XX
942         ldy     XX+1
943         jsr     icmp
944         bcc     @L12
945         rts
946 @L12:   ; plot points in 8 slices...
947         lda     XS
948         clc
949         adc     XX
950         sta     X1
951         lda     XS+1
952         adc     XX+1
953         sta     X1+1            ; x1 = xs+x
954         lda     YS
955         clc
956         adc     YY
957         sta     Y1
958         pha
959         lda     YS+1
960         adc     YY+1
961         sta     Y1+1            ; (stack)=ys+y, y1=(stack)
962         pha
963         jsr     SETPIXELCLIP    ; plot(xs+x,ys+y)
964         lda     YS
965         sec
966         sbc     YY
967         sta     Y1
968         sta     Y3
969         lda     YS+1
970         sbc     YY+1
971         sta     Y1+1            ; y3 = y1 = ys-y
972         sta     Y3+1
973         jsr     SETPIXELCLIP    ; plot(xs+x,ys-y)
974         pla
975         sta     Y1+1
976         pla
977         sta     Y1              ; y1 = ys+y
978         lda     XS
979         sec
980         sbc     XX
981         sta     X1
982         lda     XS+1
983         sbc     XX+1
984         sta     X1+1
985         jsr     SETPIXELCLIP    ; plot (xs-x,ys+y)
986         lda     Y3
987         sta     Y1
988         lda     Y3+1
989         sta     Y1+1
990         jsr     SETPIXELCLIP    ; plot (xs-x,ys-y)
991
992         lda     XS
993         clc
994         adc     YY
995         sta     X1
996         lda     XS+1
997         adc     YY+1
998         sta     X1+1            ; x1 = xs+y
999         lda     YS
1000         clc
1001         adc     XX
1002         sta     Y1
1003         pha
1004         lda     YS+1
1005         adc     XX+1
1006         sta     Y1+1            ; (stack)=ys+x, y1=(stack)
1007         pha
1008         jsr     SETPIXELCLIP    ; plot(xs+y,ys+x)
1009         lda     YS
1010         sec
1011         sbc     XX
1012         sta     Y1
1013         sta     Y3
1014         lda     YS+1
1015         sbc     XX+1
1016         sta     Y1+1            ; y3 = y1 = ys-x
1017         sta     Y3+1
1018         jsr     SETPIXELCLIP    ; plot(xs+y,ys-x)
1019         pla
1020         sta     Y1+1
1021         pla
1022         sta     Y1              ; y1 = ys+x(stack)
1023         lda     XS
1024         sec
1025         sbc     YY
1026         sta     X1
1027         lda     XS+1
1028         sbc     YY+1
1029         sta     X1+1
1030         jsr     SETPIXELCLIP    ; plot (xs-y,ys+x)
1031         lda     Y3
1032         sta     Y1
1033         lda     Y3+1
1034         sta     Y1+1
1035         jsr     SETPIXELCLIP    ; plot (xs-y,ys-x)
1036
1037         ; og = mo+y+y+1
1038         lda     MaxO
1039         ldx     MaxO+1
1040         clc
1041         adc     YY
1042         tay
1043         txa
1044         adc     YY+1
1045         tax
1046         tya
1047         clc
1048         adc     YY
1049         tay
1050         txa
1051         adc     YY+1
1052         tax
1053         tya
1054         clc
1055         adc     #1
1056         bcc     @L0143
1057         inx
1058 @L0143: sta     OGora
1059         stx     OGora+1
1060         ; ou = og-x-x+1
1061         sec
1062         sbc     XX
1063         tay
1064         txa
1065         sbc     XX+1
1066         tax
1067         tya
1068         sec
1069         sbc     XX
1070         tay
1071         txa
1072         sbc     XX+1
1073         tax
1074         tya
1075         clc
1076         adc     #1
1077         bcc     @L0146
1078         inx
1079 @L0146: sta     OUkos
1080         stx     OUkos+1
1081         ; ++y
1082         inc     YY
1083         bne     @L0148
1084         inc     YY+1
1085 @L0148: ; if (abs(ou)<abs(og))
1086         lda     OUkos
1087         ldy     OUkos+1
1088         jsr     abs
1089         sta     TEMP3
1090         sty     TEMP4
1091         lda     OGora
1092         ldy     OGora+1
1093         jsr     abs
1094         ldx     #TEMP3
1095         jsr     icmp
1096         bpl     @L0149
1097         ; { --x;
1098         sec
1099         lda     XX
1100         sbc     #1
1101         sta     XX
1102         bcs     @L014E
1103         dec     XX+1
1104 @L014E: ; mo = ou; }
1105         lda     OUkos
1106         ldx     OUkos+1
1107         jmp     @L014G
1108         ; else { mo = og }
1109 @L0149: lda     OGora
1110         ldx     OGora+1
1111 @L014G: sta     MaxO
1112         stx     MaxO+1
1113         ; }
1114         jmp     @L013B
1115
1116 ; ------------------------------------------------------------------------
1117 ; TEXTSTYLE: Set the style used when calling OUTTEXT. Text scaling in X and Y
1118 ; direction is passend in X/Y, the text direction is passed in A.
1119 ;
1120 ; Must set an error code: NO
1121 ;
1122
1123 TEXTSTYLE:
1124         stx     TEXTMAGX
1125         sty     TEXTMAGY
1126         sta     TEXTDIR
1127         rts
1128
1129
1130 ; ------------------------------------------------------------------------
1131 ; OUTTEXT: Output text at X/Y = ptr1/ptr2 using the current color and the
1132 ; current text style. The text to output is given as a zero terminated
1133 ; string with address in ptr3.
1134 ;
1135 ; Must set an error code: NO
1136 ;
1137
1138 OUTTEXT:
1139         rts
1140
1141 ; ------------------------------------------------------------------------
1142 ; Calculate all variables to plot the pixel at X1/Y1.
1143 ;------------------------
1144 ;< X1,Y1 - pixel
1145 ;> ADDR - address of card
1146 ;> X - bit number (X1 & 7)
1147 CALC:
1148         lda     Y1+1
1149         sta     ADDR+1
1150         lda     Y1
1151         asl
1152         rol     ADDR+1
1153         asl
1154         rol     ADDR+1          ; Y*4
1155         clc
1156         adc     Y1
1157         sta     ADDR
1158         lda     Y1+1
1159         adc     ADDR+1
1160         sta     ADDR+1          ; Y*4+Y=Y*5
1161         lda     ADDR
1162         asl
1163         rol     ADDR+1
1164         asl
1165         rol     ADDR+1
1166         asl
1167         rol     ADDR+1
1168         asl
1169         rol     ADDR+1
1170         sta     ADDR            ; Y*5*16=Y*80
1171         lda     X1+1
1172         sta     TEMP
1173         lda     X1
1174         lsr     TEMP
1175         ror
1176         lsr     TEMP
1177         ror
1178         lsr     TEMP
1179         ror
1180         clc
1181         adc     ADDR
1182         sta     ADDR
1183         lda     ADDR+1          ; ADDR = Y*80+x/8
1184         adc     TEMP
1185         sta     ADDR+1
1186         lda     ADDR+1
1187         adc     SCRBASE
1188         sta     ADDR+1
1189         lda     X1
1190         and     #7
1191         tax
1192         rts
1193
1194 ;-------------
1195 ; copies of some runtime routines
1196
1197 abs:
1198         ; a/y := abs(a/y)
1199         dey
1200         iny
1201         bpl     @L1
1202         ; negay
1203         clc
1204         eor     #$ff
1205         adc     #1
1206         pha
1207         tya
1208         eor     #$ff
1209         adc     #0
1210         tay
1211         pla
1212 @L1:    rts
1213
1214 icmp:
1215         ; compare a/y to zp,x
1216         sta     TEMP            ; TEMP/TEMP2 - arg2
1217         sty     TEMP2
1218         lda     $0,x
1219         pha
1220         lda     $1,x
1221         tay
1222         pla
1223         tax
1224         tya                     ; x/a - arg1 (a=high)
1225
1226         sec
1227         sbc     TEMP2
1228         bne     @L4
1229         cpx     TEMP
1230         beq     @L3
1231         adc     #$ff
1232         ora     #$01
1233 @L3:    rts
1234 @L4:    bvc     @L3
1235         eor     #$ff
1236         ora     #$01
1237         rts
1238
1239 ;-------------
1240 ; VDC helpers
1241
1242 VDCSetSourceAddr:
1243         ldx     #VDC_DATA_LO
1244         stx     VDC_ADDR_REG
1245 @L0:    bit     VDC_ADDR_REG
1246         bpl     @L0
1247         sta     VDC_DATA_REG
1248         dex
1249         tya
1250 @L1:    bit     VDC_ADDR_REG
1251         bpl     @L1
1252         stx     VDC_ADDR_REG
1253         sta     VDC_DATA_REG
1254         rts
1255
1256 VDCReadByte:
1257         ldx     #VDC_DATA
1258 VDCReadReg:
1259         stx     VDC_ADDR_REG
1260 @L0:    bit     VDC_ADDR_REG
1261         bpl     @L0
1262         lda     VDC_DATA_REG
1263         rts
1264
1265 VDCWriteByte:
1266         ldx     #VDC_DATA
1267 VDCWriteReg:
1268         stx     VDC_ADDR_REG
1269 @L0:    bit     VDC_ADDR_REG
1270         bpl     @L0
1271         sta     VDC_DATA_REG
1272         rts
1273