]> git.sur5r.net Git - cc65/blob - libsrc/c128/c128-640-480-2.s
Oliver Schmidt fixed errors in the LINE and SETPIXELCLIP 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 ; 2004-04-04, Greg King
7 ;
8 ; NOTES:
9 ; For any smart monkey that will try to optimize this: PLEASE do tests on
10 ; real VDC, not only VICE.
11 ;
12 ; Only DONE routine contains C128-mode specific stuff, everything else will
13 ; work in C64-mode of C128 (C64 needs full VDC init then).
14 ;
15 ; With special initialization and CALC we can get 320x200 double-pixel mode.
16 ;
17 ; Color translation values for BROWN and GRAY3 are obviously wrong, they
18 ; could be replaced by equiv. of ORANGE and GRAY2 but this would give only
19 ; 14 of 16 colors available.
20 ;
21 ; Register 25 ($19) is said to require different value for VDC v1, but I
22 ; couldn't find what 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   TGI_API_VERSION         ; TGI version number
60 xres:   .word   640                     ; X resolution
61 yres:   .word   480                     ; Y resolution
62         .byte   2                       ; Number of drawing colors
63 pages:  .byte   0                       ; 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).
70
71         .addr   INSTALL
72         .addr   UNINSTALL
73         .addr   INIT
74         .addr   DONE
75         .addr   GETERROR
76         .addr   CONTROL
77         .addr   CLEAR
78         .addr   SETVIEWPAGE
79         .addr   SETDRAWPAGE
80         .addr   SETCOLOR
81         .addr   SETPALETTE
82         .addr   GETPALETTE
83         .addr   GETDEFPALETTE
84         .addr   SETPIXEL
85         .addr   GETPIXEL
86         .addr   LINE
87         .addr   BAR
88         .addr   CIRCLE
89         .addr   TEXTSTYLE
90         .addr   OUTTEXT
91         .addr   0                       ; IRQ entry is unused
92
93 ; ------------------------------------------------------------------------
94 ; Data.
95
96 ; Variables mapped to the zero page segment variables. Some of these are
97 ; used for passing parameters to the driver.
98
99 X1              = ptr1
100 Y1              = ptr2
101 X2              = ptr3
102 Y2              = ptr4
103 RADIUS          = tmp1
104
105 ADDR            = tmp1          ; (2)   CALC
106 TEMP            = tmp3          ;       CALC icmp
107 TEMP2           = tmp4          ;       icmp
108 TEMP3           = sreg          ;       LINE
109 TEMP4           = sreg+1        ;       LINE
110
111 ; Line routine stuff (must be on zpage)
112 PB              = ptr3          ; (2)   LINE
113 UB              = ptr4          ; (2)   LINE
114 ERR             = regsave       ; (2)   LINE
115 NX              = regsave+2     ; (2)   LINE
116 ; Circle stuff
117 XX              = ptr3          ; (2)   CIRCLE
118 YY              = ptr4          ; (2)   CIRCLE
119 MaxO            = sreg          ; (overwritten by TEMP3+TEMP4, but restored from OG/OU anyway)
120 XS              = regsave       ; (2)   CIRCLE
121 YS              = regsave+2     ; (2)   CIRCLE
122
123 ; Absolute variables used in the code
124
125 .bss
126
127 ERROR:          .res    1       ; Error code
128 PALETTE:        .res    2       ; The current palette
129
130 BITMASK:        .res    1       ; $00 = clear, $FF = set pixels
131
132 OLDCOLOR:       .res    1       ; colors before entering gfx mode
133
134 ; Line routine stuff (combined with CIRCLE to save space)
135
136 OGora:
137 COUNT:           .res   2
138 OUkos:
139 NY:              .res   2
140 Y3:
141 DX:              .res   1
142 DY:              .res   1
143 AX:              .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: NO
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         jmp VDCWriteReg         ; switch to text screen
359
360 ; ------------------------------------------------------------------------
361 ; GETERROR: Return the error code in A and clear it.
362
363 GETERROR:
364         ldx     #TGI_ERR_OK
365         lda     ERROR
366         stx     ERROR
367         rts
368
369 ; ------------------------------------------------------------------------
370 ; CONTROL: Platform/driver specific entry point.
371 ;
372 ; Must set an error code: YES
373 ;
374
375 CONTROL:
376         lda     #TGI_ERR_INV_FUNC
377         sta     ERROR
378         rts
379
380 ; ------------------------------------------------------------------------
381 ; CLEAR: Clears the screen.
382 ;
383 ; Must set an error code: NO
384 ;
385
386 CLEAR:
387         lda     #0
388         tay
389         jsr     VDCSetSourceAddr
390         lda     #0
391         ldx     #VDC_VSCROLL
392         jsr     VDCWriteReg                     ; set fill mode
393         lda     #0
394         jsr     VDCWriteByte                    ; put 1rst byte (fill value)
395         ldy     #159                            ; 159 times
396         lda     #0                              ; 256 bytes
397         ldx     #VDC_COUNT
398 @L1:    jsr     VDCWriteReg
399         dey
400         bne     @L1
401         rts
402
403 ; ------------------------------------------------------------------------
404 ; SETVIEWPAGE: Set the visible page. Called with the new page in A (0..n).
405 ; The page number is already checked to be valid by the graphics kernel.
406 ;
407 ; Must set an error code: NO (will only be called if page ok)
408 ;
409
410 SETVIEWPAGE:
411         rts
412
413 ; ------------------------------------------------------------------------
414 ; SETDRAWPAGE: Set the drawable page. Called with the new page in A (0..n).
415 ; The page number is already checked to be valid by the graphics kernel.
416 ;
417 ; Must set an error code: NO (will only be called if page ok)
418 ;
419
420 SETDRAWPAGE:
421         rts
422
423 ; ------------------------------------------------------------------------
424 ; SETCOLOR: Set the drawing color (in A). The new color is already checked
425 ; to be in a valid range (0..maxcolor-1).
426 ;
427 ; Must set an error code: NO (will only be called if color ok)
428 ;
429
430 SETCOLOR:
431         tax
432         beq     @L1
433         lda     #$FF
434 @L1:    sta     BITMASK
435         rts
436
437 ; ------------------------------------------------------------------------
438 ; SETPALETTE: Set the palette (not available with all drivers/hardware).
439 ; A pointer to the palette is passed in ptr1. Must set an error if palettes
440 ; are not supported
441 ;
442 ; Must set an error code: YES
443 ;
444
445 SETPALETTE:
446         ldy     #PALETTESIZE - 1
447 @L1:    lda     (ptr1),y        ; Copy the palette
448         and     #$0F            ; Make a valid color
449         sta     PALETTE,y
450         dey
451         bpl     @L1
452
453 ; Get the color entries from the palette
454
455         ldy     PALETTE+1       ; Foreground color
456         lda     COLTRANS,y
457         asl     a
458         asl     a
459         asl     a
460         asl     a
461         ldy     PALETTE         ; Background color
462         ora     COLTRANS,y
463
464         ldx     #VDC_COLORS
465         jsr     VDCWriteReg
466         lda     #TGI_ERR_OK     ; Clear error code
467         sta     ERROR
468         rts
469
470 ; ------------------------------------------------------------------------
471 ; GETPALETTE: Return the current palette in A/X. Even drivers that cannot
472 ; set the palette should return the default palette here, so there's no
473 ; way for this function to fail.
474 ;
475 ; Must set an error code: NO
476 ;
477
478 GETPALETTE:
479         lda     #<PALETTE
480         ldx     #>PALETTE
481         rts
482
483 ; ------------------------------------------------------------------------
484 ; GETDEFPALETTE: Return the default palette for the driver in A/X. All
485 ; drivers should return something reasonable here, even drivers that don't
486 ; support palettes, otherwise the caller has no way to determine the colors
487 ; of the (not changeable) palette.
488 ;
489 ; Must set an error code: NO (all drivers must have a default palette)
490 ;
491
492 GETDEFPALETTE:
493         lda     #<DEFPALETTE
494         ldx     #>DEFPALETTE
495         rts
496
497 ; ------------------------------------------------------------------------
498 ; SETPIXEL: Draw one pixel at X1/Y1 = ptr1/ptr2 with the current drawing
499 ; color. The coordinates passed to this function are never outside the
500 ; visible screen area, so there is no need for clipping inside this function.
501 ;
502 ; Must set an error code: NO
503 ;
504
505 SETPIXELCLIP:
506         lda     Y1+1
507         bmi     @finito         ; y<0
508         lda     X1+1
509         bmi     @finito         ; x<0
510         lda     X1
511         ldx     X1+1
512         sta     ADDR
513         stx     ADDR+1
514         ldx     #ADDR
515         lda     xres
516         ldy     xres+1
517         jsr     icmp            ; ( x < xres ) ...
518         bcs     @finito
519         lda     Y1
520         ldx     Y1+1
521         sta     ADDR
522         stx     ADDR+1
523         ldx     #ADDR
524         lda     yres
525         ldy     yres+1
526         jsr     icmp            ; ... && ( y < yres )
527         bcc     SETPIXEL
528 @finito:rts
529
530 SETPIXEL:
531         jsr     CALC            ; Calculate coordinates
532
533         stx     TEMP
534         lda     ADDR
535         ldy     ADDR+1
536         jsr     VDCSetSourceAddr
537         jsr     VDCReadByte
538         ldx     TEMP
539
540         sta     TEMP
541         eor     BITMASK
542         and     BITTAB,X
543         eor     TEMP
544         pha
545         lda     ADDR
546         ldy     ADDR+1
547         jsr     VDCSetSourceAddr
548         pla
549         jsr     VDCWriteByte
550
551 @L9:    rts
552
553 ; ------------------------------------------------------------------------
554 ; GETPIXEL: Read the color value of a pixel and return it in A/X. The
555 ; coordinates passed to this function are never outside the visible screen
556 ; area, so there is no need for clipping inside this function.
557
558
559 GETPIXEL:
560         jsr     CALC            ; Calculate coordinates
561
562         stx     TEMP            ; preserve X
563         lda     ADDR
564         ldy     ADDR+1
565         jsr     VDCSetSourceAddr
566         jsr     VDCReadByte
567         ldx     TEMP
568
569         ldy     #$00
570         and     BITTAB,X
571         beq     @L1
572         iny
573
574 @L1:    tya                     ; Get color value into A
575         ldx     #$00            ; Clear high byte
576         rts
577
578 ; ------------------------------------------------------------------------
579 ; LINE: Draw a line from X1/Y1 to X2/Y2, where X1/Y1 = ptr1/ptr2 and
580 ; X2/Y2 = ptr3/ptr4 using the current drawing color.
581 ;
582 ; Must set an error code: NO
583 ;
584
585 LINE:
586         ; nx = abs(x2 - x1)
587         lda     X2
588         sec
589         sbc     X1
590         sta     NX
591         lda     X2+1
592         sbc     X1+1
593         tay
594         lda     NX
595         jsr     abs
596         sta     NX
597         sty     NX+1
598         ; ny = abs(y2 - y1)
599         lda     Y2
600         sec
601         sbc     Y1
602         sta     NY
603         lda     Y2+1
604         sbc     Y1+1
605         tay
606         lda     NY
607         jsr     abs
608         sta     NY
609         sty     NY+1
610         ; if (x2>=x1)
611         ldx     #X2
612         lda     X1
613         ldy     X1+1
614         jsr     icmp
615         bcc     @L0243
616         ; dx = 1;
617         lda     #1
618         bne     @L0244
619         ; else
620         ; dx = -1;
621 @L0243: lda     #$ff
622 @L0244: sta     DX
623         ; if (y2>=y1)
624         ldx     #Y2
625         lda     Y1
626         ldy     Y1+1
627         jsr     icmp
628         bcc     @L024A
629         ; dy = 1;
630         lda     #1
631         bne     @L024B
632         ; else
633         ; dy = -1;
634 @L024A: lda     #$ff
635 @L024B: sta     DY
636         ; err = ax = ay = 0;
637         lda     #0
638         sta     ERR
639         sta     ERR+1
640         sta     AX
641         sta     AY
642
643         ; if (nx<ny) {
644         ldx     #NX
645         lda     NY
646         ldy     NY+1
647         jsr     icmp
648         bcs     @L0255
649         ;  nx <-> ny
650         lda     NX
651         ldx     NY
652         sta     NY
653         stx     NX
654         lda     NX+1
655         ldx     NY+1
656         sta     NY+1
657         stx     NX+1
658         ; ax = dx
659         lda     DX
660         sta     AX
661         ; ay = dy
662         lda     DY
663         sta     AY
664         ; dx = dy = 0;
665         lda     #0
666         sta     DX
667         sta     DY
668         ; ny = - ny;
669 @L0255: lda     NY
670         ldy     NY+1
671         jsr     neg
672         sta     NY
673         sty     NY+1
674         ; for (count=nx;count>0;--count) {
675         lda     NX
676         ldx     NX+1
677         sta     COUNT
678         stx     COUNT+1
679 @L0166: lda     COUNT           ; count>0
680         ora     COUNT+1
681         bne     @L0167
682         rts
683         ;    setpixel(X1,Y1)
684 @L0167: jsr     SETPIXELCLIP
685         ;    pb = err + ny
686         lda     ERR
687         clc
688         adc     NY
689         sta     PB
690         lda     ERR+1
691         adc     NY+1
692         sta     PB+1
693         tax
694         ;    ub = pb + nx
695         lda     PB
696         clc
697         adc     NX
698         sta     UB
699         txa
700         adc     NX+1
701         sta     UB+1
702         ;    x1 = x1 + dx
703         ldx     #0
704         lda     DX
705         bpl     @L027B
706         dex
707 @L027B: clc
708         adc     X1
709         sta     X1
710         txa
711         adc     X1+1
712         sta     X1+1
713         ;   y1 = y1 + ay
714         ldx     #0
715         lda     AY
716         bpl     @L027E
717         dex
718 @L027E: clc
719         adc     Y1
720         sta     Y1
721         txa
722         adc     Y1+1
723         sta     Y1+1
724         ; if (abs(pb)<abs(ub)) {
725         lda     PB
726         ldy     PB+1
727         jsr     abs
728         sta     TEMP3
729         sty     TEMP4
730         lda     UB
731         ldy     UB+1
732         jsr     abs
733         ldx     #TEMP3
734         jsr     icmp
735         bpl     @L027F
736         ;   err = pb
737         lda     PB
738         ldx     PB+1
739         jmp     @L0312
740         ; } else { x1 = x1 + ax
741 @L027F:
742         ldx     #0
743         lda     AX
744         bpl     @L0288
745         dex
746 @L0288: clc
747         adc     X1
748         sta     X1
749         txa
750         adc     X1+1
751         sta     X1+1
752         ;       y1 = y1 + dy
753         ldx     #0
754         lda     DY
755         bpl     @L028B
756         dex
757 @L028B: clc
758         adc     Y1
759         sta     Y1
760         txa
761         adc     Y1+1
762         sta     Y1+1
763         ;       err = ub }
764         lda     UB
765         ldx     UB+1
766 @L0312:
767         sta     ERR
768         stx     ERR+1
769         ; } (--count)
770         sec
771         lda     COUNT
772         sbc     #1
773         sta     COUNT
774         bcc     @L0260
775         jmp     @L0166
776 @L0260: dec     COUNT+1
777         jmp     @L0166
778
779 ; ------------------------------------------------------------------------
780 ; BAR: Draw a filled rectangle with the corners X1/Y1, X2/Y2, where
781 ; X1/Y1 = ptr1/ptr2 and X2/Y2 = ptr3/ptr4 using the current drawing color.
782 ; Contrary to most other functions, the graphics kernel will sort and clip
783 ; the coordinates before calling the driver, so on entry the following
784 ; conditions are valid:
785 ;       X1 <= X2
786 ;       Y1 <= Y2
787 ;       (X1 >= 0) && (X1 < XRES)
788 ;       (X2 >= 0) && (X2 < XRES)
789 ;       (Y1 >= 0) && (Y1 < YRES)
790 ;       (Y2 >= 0) && (Y2 < YRES)
791 ;
792 ; Must set an error code: NO
793 ;
794
795 BAR:
796         inc     Y2
797         bne     HORLINE
798         inc     Y2+1
799
800 ; Original code for a horizontal line
801
802 HORLINE:
803         lda X1
804         pha
805         lda X1+1
806         pha
807         jsr CALC                ; get data for LEFT
808         lda BITMASKL,x          ; remember left address and bitmask
809         pha
810         lda ADDR
811         pha
812         lda ADDR+1
813         pha
814
815         lda X2
816         sta X1
817         lda X2+1
818         sta X1+1
819         jsr CALC                ; get data for RIGHT
820         lda BITMASKR,x
821         sta TEMP3
822
823         pla                     ; recall data for LEFT
824         sta X1+1
825         pla
826         sta X1                  ; put left address into X1
827         pla
828
829         cmp #%11111111          ; if left bit <> 0
830         beq @L1
831         sta TEMP2               ; do left byte only...
832         lda X1
833         ldy X1+1
834         jsr VDCSetSourceAddr
835         jsr VDCReadByte
836         sta TEMP
837         eor BITMASK
838         and TEMP2
839         eor TEMP
840         pha
841         lda X1
842         ldy X1+1
843         jsr VDCSetSourceAddr
844         pla
845         jsr VDCWriteByte
846         inc X1                  ; ... and proceed
847         bne @L1
848         inc X1+1
849
850         ; do right byte (if Y2=0 ++ADDR and skip)
851 @L1:    lda TEMP3
852         cmp #%11111111          ; if right bit <> 7
853         bne @L11
854         inc ADDR                ; right bit = 7 - the next one is the last
855         bne @L10
856         inc ADDR+1
857 @L10:   bne @L2
858
859 @L11:   lda ADDR                ; do right byte only...
860         ldy ADDR+1
861         jsr VDCSetSourceAddr
862         jsr VDCReadByte
863         sta TEMP
864         eor BITMASK
865         and TEMP3
866         eor TEMP
867         pha
868         lda ADDR
869         ldy ADDR+1
870         jsr VDCSetSourceAddr
871         pla
872         jsr VDCWriteByte
873
874 @L2:                            ; do the fill in the middle
875         lda ADDR                ; calculate offset in full bytes
876         sec
877         sbc X1
878         beq @L3                 ; if equal - there are no more bytes
879         sta ADDR
880
881         lda X1                  ; setup for the left side
882         ldy X1+1
883         jsr VDCSetSourceAddr
884         lda BITMASK             ; get color
885         jsr VDCWriteByte        ; put 1st value
886         ldx ADDR
887         dex
888         beq @L3                 ; 1 byte already written
889
890         stx ADDR                ; if there are more bytes - fill them...
891         ldx #VDC_VSCROLL
892         lda #0
893         jsr VDCWriteReg         ; setup for fill
894         ldx #VDC_COUNT
895         lda ADDR
896         jsr VDCWriteReg         ; ... fill them NOW!
897
898 @L3:    pla
899         sta X1+1
900         pla
901         sta X1
902
903 ; End of horizontal line code
904
905         inc     Y1
906         bne     @L4
907         inc     Y1+1
908 @L4:    lda     Y1
909         cmp     Y2
910         bne     @L5
911         lda     Y1+1
912         cmp     Y2+1
913         bne     @L5
914         rts
915
916 @L5:    jmp     HORLINE
917
918
919 ; ------------------------------------------------------------------------
920 ; CIRCLE: Draw a circle around the center X1/Y1 (= ptr1/ptr2) with the
921 ; radius in tmp1 and the current drawing color.
922 ;
923 ; Must set an error code: NO
924 ;
925
926 CIRCLE:
927         lda     RADIUS
928         bne     @L1
929         jmp     SETPIXELCLIP    ; Plot as a point
930
931 @L1:    sta     XX
932         ; x = r;
933         lda     #0
934         sta     XX+1
935         sta     YY
936         sta     YY+1
937         sta     MaxO
938         sta     MaxO+1
939         ; y =0; mo=0;
940         lda     X1
941         ldx     X1+1
942         sta     XS
943         stx     XS+1
944         lda     Y1
945         ldx     Y1+1
946         sta     YS
947         stx     YS+1            ; XS/YS to remember the center
948
949         ; while (y<x) {
950 @L013B: ldx     #YY
951         lda     XX
952         ldy     XX+1
953         jsr     icmp
954         bcc     @L12
955         rts
956 @L12:   ; plot points in 8 slices...
957         lda     XS
958         clc
959         adc     XX
960         sta     X1
961         lda     XS+1
962         adc     XX+1
963         sta     X1+1            ; x1 = xs+x
964         lda     YS
965         clc
966         adc     YY
967         sta     Y1
968         pha
969         lda     YS+1
970         adc     YY+1
971         sta     Y1+1            ; (stack)=ys+y, y1=(stack)
972         pha
973         jsr     SETPIXELCLIP    ; plot(xs+x,ys+y)
974         lda     YS
975         sec
976         sbc     YY
977         sta     Y1
978         sta     Y3
979         lda     YS+1
980         sbc     YY+1
981         sta     Y1+1            ; y3 = y1 = ys-y
982         sta     Y3+1
983         jsr     SETPIXELCLIP    ; plot(xs+x,ys-y)
984         pla
985         sta     Y1+1
986         pla
987         sta     Y1              ; y1 = ys+y
988         lda     XS
989         sec
990         sbc     XX
991         sta     X1
992         lda     XS+1
993         sbc     XX+1
994         sta     X1+1
995         jsr     SETPIXELCLIP    ; plot (xs-x,ys+y)
996         lda     Y3
997         sta     Y1
998         lda     Y3+1
999         sta     Y1+1
1000         jsr     SETPIXELCLIP    ; plot (xs-x,ys-y)
1001
1002         lda     XS
1003         clc
1004         adc     YY
1005         sta     X1
1006         lda     XS+1
1007         adc     YY+1
1008         sta     X1+1            ; x1 = xs+y
1009         lda     YS
1010         clc
1011         adc     XX
1012         sta     Y1
1013         pha
1014         lda     YS+1
1015         adc     XX+1
1016         sta     Y1+1            ; (stack)=ys+x, y1=(stack)
1017         pha
1018         jsr     SETPIXELCLIP    ; plot(xs+y,ys+x)
1019         lda     YS
1020         sec
1021         sbc     XX
1022         sta     Y1
1023         sta     Y3
1024         lda     YS+1
1025         sbc     XX+1
1026         sta     Y1+1            ; y3 = y1 = ys-x
1027         sta     Y3+1
1028         jsr     SETPIXELCLIP    ; plot(xs+y,ys-x)
1029         pla
1030         sta     Y1+1
1031         pla
1032         sta     Y1              ; y1 = ys+x(stack)
1033         lda     XS
1034         sec
1035         sbc     YY
1036         sta     X1
1037         lda     XS+1
1038         sbc     YY+1
1039         sta     X1+1
1040         jsr     SETPIXELCLIP    ; plot (xs-y,ys+x)
1041         lda     Y3
1042         sta     Y1
1043         lda     Y3+1
1044         sta     Y1+1
1045         jsr     SETPIXELCLIP    ; plot (xs-y,ys-x)
1046
1047         ; og = mo+y+y+1
1048         lda     MaxO
1049         ldx     MaxO+1
1050         clc
1051         adc     YY
1052         tay
1053         txa
1054         adc     YY+1
1055         tax
1056         tya
1057         clc
1058         adc     YY
1059         tay
1060         txa
1061         adc     YY+1
1062         tax
1063         tya
1064         clc
1065         adc     #1
1066         bcc     @L0143
1067         inx
1068 @L0143: sta     OGora
1069         stx     OGora+1
1070         ; ou = og-x-x+1
1071         sec
1072         sbc     XX
1073         tay
1074         txa
1075         sbc     XX+1
1076         tax
1077         tya
1078         sec
1079         sbc     XX
1080         tay
1081         txa
1082         sbc     XX+1
1083         tax
1084         tya
1085         clc
1086         adc     #1
1087         bcc     @L0146
1088         inx
1089 @L0146: sta     OUkos
1090         stx     OUkos+1
1091         ; ++y
1092         inc     YY
1093         bne     @L0148
1094         inc     YY+1
1095 @L0148: ; if (abs(ou)<abs(og))
1096         lda     OUkos
1097         ldy     OUkos+1
1098         jsr     abs
1099         sta     TEMP3
1100         sty     TEMP4
1101         lda     OGora
1102         ldy     OGora+1
1103         jsr     abs
1104         ldx     #TEMP3
1105         jsr     icmp
1106         bpl     @L0149
1107         ; { --x;
1108         sec
1109         lda     XX
1110         sbc     #1
1111         sta     XX
1112         bcs     @L014E
1113         dec     XX+1
1114 @L014E: ; mo = ou; }
1115         lda     OUkos
1116         ldx     OUkos+1
1117         jmp     @L014G
1118         ; else { mo = og }
1119 @L0149: lda     OGora
1120         ldx     OGora+1
1121 @L014G: sta     MaxO
1122         stx     MaxO+1
1123         ; }
1124         jmp     @L013B
1125
1126 ; ------------------------------------------------------------------------
1127 ; TEXTSTYLE: Set the style used when calling OUTTEXT. Text scaling in X and Y
1128 ; direction is passend in X/Y, the text direction is passed in A.
1129 ;
1130 ; Must set an error code: NO
1131 ;
1132
1133 TEXTSTYLE:
1134         stx     TEXTMAGX
1135         sty     TEXTMAGY
1136         sta     TEXTDIR
1137         rts
1138
1139
1140 ; ------------------------------------------------------------------------
1141 ; OUTTEXT: Output text at X/Y = ptr1/ptr2 using the current color and the
1142 ; current text style. The text to output is given as a zero terminated
1143 ; string with address in ptr3.
1144 ;
1145 ; Must set an error code: NO
1146 ;
1147
1148 OUTTEXT:
1149         rts
1150
1151 ; ------------------------------------------------------------------------
1152 ; Calculate all variables to plot the pixel at X1/Y1.
1153 ;------------------------
1154 ;< X1,Y1 - pixel
1155 ;> ADDR - address of card
1156 ;> X - bit number (X1 & 7)
1157 CALC:
1158         lda     Y1
1159         pha
1160         lda     Y1+1
1161         pha
1162         lsr
1163         ror     Y1              ; Y=Y/2
1164         sta     Y1+1
1165         sta     ADDR+1
1166         lda     Y1
1167         asl
1168         rol     ADDR+1
1169         asl
1170         rol     ADDR+1          ; Y*4
1171         clc
1172         adc     Y1
1173         sta     ADDR
1174         lda     Y1+1
1175         adc     ADDR+1
1176         sta     ADDR+1          ; Y*4+Y=Y*5
1177         lda     ADDR
1178         asl
1179         rol     ADDR+1
1180         asl
1181         rol     ADDR+1
1182         asl
1183         rol     ADDR+1
1184         asl
1185         rol     ADDR+1
1186         sta     ADDR            ; Y*5*16=Y*80
1187         lda     X1+1
1188         sta     TEMP
1189         lda     X1
1190         lsr     TEMP
1191         ror
1192         lsr     TEMP
1193         ror
1194         lsr     TEMP
1195         ror
1196         clc
1197         adc     ADDR
1198         sta     ADDR
1199         lda     ADDR+1          ; ADDR = Y*80+x/8
1200         adc     TEMP
1201         sta     ADDR+1
1202         pla
1203         sta     Y1+1
1204         pla
1205         sta     Y1
1206         and     #1
1207         beq     @even           ; even line - no offset
1208         lda     ADDR
1209         clc
1210         adc     #<21360
1211         sta     ADDR
1212         lda     ADDR+1
1213         adc     #>21360
1214         sta     ADDR+1          ; odd lines are 21360 bytes farther
1215 @even:  lda     X1
1216         and     #7
1217         tax
1218         rts
1219
1220 ;-------------
1221 ; copies of some runtime routines
1222
1223 abs:
1224         ; a/y := abs(a/y)
1225         cpy     #$00
1226         bpl     absend
1227         ; negay
1228 neg:    eor     #$ff
1229         add     #1
1230         pha
1231         tya
1232         eor     #$ff
1233         adc     #0
1234         tay
1235         pla
1236 absend: rts
1237
1238 icmp:
1239         ; compare a/y to zp,x
1240         sta     TEMP            ; TEMP/TEMP2 - arg2
1241         sty     TEMP2
1242         lda     $0,x
1243         pha
1244         lda     $1,x
1245         tay
1246         pla
1247         tax
1248         tya                     ; x/a - arg1 (a=high)
1249
1250         sec
1251         sbc     TEMP2
1252         bne     @L4
1253         cpx     TEMP
1254         beq     @L3
1255         adc     #$ff
1256         ora     #$01
1257 @L3:    rts
1258 @L4:    bvc     @L3
1259         eor     #$ff
1260         ora     #$01
1261         rts
1262
1263 ;-------------
1264 ; VDC helpers
1265
1266 VDCSetSourceAddr:
1267         pha
1268         tya
1269         ldx     #VDC_DATA_HI
1270         jsr     VDCWriteReg
1271         pla
1272         ldx     #VDC_DATA_LO
1273         bne     VDCWriteReg
1274
1275 VDCReadByte:
1276         ldx     #VDC_DATA
1277 VDCReadReg:
1278         stx     VDC_ADDR_REG
1279 @L0:    bit     VDC_ADDR_REG
1280         bpl     @L0
1281         lda     VDC_DATA_REG
1282         rts
1283
1284 VDCWriteByte:
1285         ldx     #VDC_DATA
1286 VDCWriteReg:
1287         stx     VDC_ADDR_REG
1288 @L0:    bit     VDC_ADDR_REG
1289         bpl     @L0
1290         sta     VDC_DATA_REG
1291         rts
1292