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