]> git.sur5r.net Git - cc65/blob - libsrc/c128/tgi/c128-vdc.s
5100f7f7d0fef66f54f18759c1dd5707e023a761
[cc65] / libsrc / c128 / tgi / c128-vdc.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 ; 2004-04-04, Greg King
6 ;
7 ; NOTES:
8 ; For any smart monkey that will try to optimize this: PLEASE do tests on
9 ; real VDC, not only VICE.
10 ;
11 ; Only DONE routine contains C128-mode specific stuff, everything else will
12 ; work in 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
17 ; could be replaced by equiv. of ORANGE and GRAY2 but this would give only
18 ; 14 of 16 colors available.
19 ;
20 ; Register 25 ($19) is said to require different value for VDC v1, but I
21 ; couldn't find what it should be.
22
23         .include        "zeropage.inc"
24
25         .include        "tgi-kernel.inc"
26         .include        "tgi-error.inc"
27
28         .macpack        generic
29         .macpack        module
30
31
32 ; ------------------------------------------------------------------------
33 ; Constants
34
35 VDC_ADDR_REG      = $D600                 ; VDC address
36 VDC_DATA_REG      = $D601                 ; VDC data
37
38 VDC_DSP_HI        = 12                    ; registers used
39 VDC_DSP_LO        = 13
40 VDC_DATA_HI       = 18
41 VDC_DATA_LO       = 19
42 VDC_VSCROLL       = 24
43 VDC_HSCROLL       = 25
44 VDC_COLORS        = 26
45 VDC_CSET          = 28
46 VDC_COUNT         = 30
47 VDC_DATA          = 31
48
49 ; ------------------------------------------------------------------------
50 ; Header. Includes jump table and constants.
51
52         module_header   _c128_vdc_tgi
53
54 ; First part of the header is a structure that has a magic and defines the
55 ; capabilities of the driver
56
57         .byte   $74, $67, $69           ; "tgi"
58         .byte   TGI_API_VERSION         ; TGI API version number
59         .addr   $0000                   ; Library reference
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         .word   $006A                   ; Aspect ratio (based on 4/3 display)
67         .byte   0                       ; TGI driver flags
68
69 ; Next comes the jump table. Currently all entries must be valid and may point
70 ; to an RTS for test versions (function not implemented).
71
72         .addr   INSTALL
73         .addr   UNINSTALL
74         .addr   INIT
75         .addr   DONE
76         .addr   GETERROR
77         .addr   CONTROL
78         .addr   CLEAR
79         .addr   SETVIEWPAGE
80         .addr   SETDRAWPAGE
81         .addr   SETCOLOR
82         .addr   SETPALETTE
83         .addr   GETPALETTE
84         .addr   GETDEFPALETTE
85         .addr   SETPIXEL
86         .addr   GETPIXEL
87         .addr   LINE
88         .addr   BAR
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
104 ADDR            = tmp1
105 TEMP            = tmp3
106 TEMP2           = tmp4          ; HORLINE
107 TEMP3           = sreg          ; HORLINE
108
109 ; Absolute variables used in the code
110
111 .bss
112
113 SCRBASE:        .res    1       ; High byte of screen base
114
115 ERROR:          .res    1       ; Error code
116 PALETTE:        .res    2       ; The current palette
117
118 BITMASK:        .res    1       ; $00 = clear, $FF = set pixels
119
120 OLDCOLOR:       .res    1       ; colors before entering gfx mode
121
122 ; Text output stuff
123 TEXTMAGX:       .res    1
124 TEXTMAGY:       .res    1
125 TEXTDIR:        .res    1
126
127 ; Constants and tables
128
129 .rodata
130
131 DEFPALETTE:     .byte   $00, $0f        ; White on black
132 PALETTESIZE     = * - DEFPALETTE
133
134 BITTAB:         .byte   $80,$40,$20,$10,$08,$04,$02,$01
135
136 BITMASKL:       .byte   %11111111, %01111111, %00111111, %00011111
137                 .byte   %00001111, %00000111, %00000011, %00000001
138
139 BITMASKR:       .byte   %10000000, %11000000, %11100000, %11110000
140                 .byte   %11111000, %11111100, %11111110, %11111111
141
142 ; color translation table (indexed by VIC color)
143 COLTRANS:       .byte $00, $0f, $08, $06, $0a, $04, $02, $0c
144                 .byte $0d, $0b, $09, $01, $0e, $05, $03, $07
145                 ; colors BROWN and GRAY3 are wrong
146
147 ; VDC initialization table (reg),(val),...,$ff
148 InitVDCTab:
149                 .byte VDC_DSP_HI, 0             ; viewpage 0 as default
150                 .byte VDC_DSP_LO, 0
151                 .byte VDC_HSCROLL, $87
152                 .byte $ff
153
154 SCN80CLR:       .byte 27,88,147,27,88,0
155
156 .code
157
158 ; ------------------------------------------------------------------------
159 ; INSTALL routine. Is called after the driver is loaded into memory. May
160 ; initialize anything that has to be done just once. Is probably empty
161 ; most of the time.
162 ;
163 ; Must set an error code: NO
164 ;
165
166 INSTALL:
167         ; check for VDC version and update register $19 value
168
169         ; check for VDC ram size and update number of available screens
170
171         ldx     #VDC_CSET       ; determine size of RAM...
172         jsr     VDCReadReg
173         sta     tmp1
174         ora     #%00010000
175         jsr     VDCWriteReg     ; turn on 64k
176
177         jsr     settestadr1     ; save original value of test byte
178         jsr     VDCReadByte
179         sta     tmp2
180
181         lda     #$55            ; write $55 here
182         ldy     #ptr1
183         jsr     test64k         ; read it here and there
184         lda     #$aa            ; write $aa here
185         ldy     #ptr2
186         jsr     test64k         ; read it here and there
187
188         jsr     settestadr1
189         lda     tmp2
190         jsr     VDCWriteByte    ; restore original value of test byte
191
192         lda     ptr1            ; do bytes match?
193         cmp     ptr1+1
194         bne     @have64k
195         lda     ptr2
196         cmp     ptr2+1
197         bne     @have64k
198
199         ldx     #VDC_CSET
200         lda     tmp1
201         jsr     VDCWriteReg     ; restore 16/64k flag
202         jmp     @endok          ; and leave default values for 16k
203
204 @have64k:
205         lda     #4
206         sta     pages
207 @endok:
208         lda     #0
209         sta     SCRBASE         ; draw page 0 as default
210         rts
211
212 test64k:
213         sta     tmp1
214         sty     ptr3
215         lda     #0
216         sta     ptr3+1
217         jsr     settestadr1
218         lda     tmp1
219         jsr     VDCWriteByte            ; write $55
220         jsr     settestadr1
221         jsr     VDCReadByte             ; read here
222         pha
223         jsr     settestadr2
224         jsr     VDCReadByte             ; and there
225         ldy     #1
226         sta     (ptr3),y
227         pla
228         dey
229         sta     (ptr3),y
230         rts
231
232 settestadr1:
233         ldy     #$02                    ; test page 2 (here)
234         .byte   $2c
235 settestadr2:
236         ldy     #$42                    ; or page 64+2 (there)
237         lda     #0
238         jmp     VDCSetSourceAddr
239
240 ; ------------------------------------------------------------------------
241 ; UNINSTALL routine. Is called before the driver is removed from memory. May
242 ; clean up anything done by INSTALL but is probably empty most of the time.
243 ;
244 ; Must set an error code: NO
245 ;
246
247 UNINSTALL:
248         rts
249
250
251 ; ------------------------------------------------------------------------
252 ; INIT: Changes an already installed device from text mode to graphics
253 ; mode.
254 ; Note that INIT/DONE may be called multiple times while the driver
255 ; is loaded, while INSTALL is only called once, so any code that is needed
256 ; to initializes variables and so on must go here. Setting palette and
257 ; clearing the screen is not needed because this is called by the graphics
258 ; kernel later.
259 ; The graphics kernel will never call INIT when a graphics mode is already
260 ; active, so there is no need to protect against that.
261 ;
262 ; Must set an error code: YES
263 ;
264
265 INIT:
266
267 ; Initialize variables
268
269 @L1:    ldx     #$FF
270         stx     BITMASK
271
272 ; Remeber current color value
273         ldx     #VDC_COLORS
274         jsr     VDCReadReg
275         sta     OLDCOLOR
276
277 ; Switch into graphics mode (set view page 0)
278
279         ldy     #0
280 @L2:    ldx     InitVDCTab,y
281         bmi     @L3
282         iny
283         lda     InitVDCTab,y
284         jsr     VDCWriteReg
285         iny
286         bne     @L2
287 @L3:
288
289 ; Done, reset the error code
290
291         lda     #TGI_ERR_OK
292         sta     ERROR
293         rts
294
295 ; ------------------------------------------------------------------------
296 ; DONE: Will be called to switch the graphics device back into text mode.
297 ; The graphics kernel will never call DONE when no graphics mode is active,
298 ; so there is no need to protect against that.
299 ;
300 ; Must set an error code: NO
301 ;
302
303 DONE:
304         ; This part is C128-mode specific
305         jsr $e179               ; reload character set and setup VDC
306         jsr $ff62
307         lda $d7                 ; in 80-columns?
308         bne @L01
309 @L0:    lda SCN80CLR,y
310         beq @L1
311         jsr $ffd2               ; print \xe,clr,\xe
312         iny
313         bne @L0
314 @L01:   lda #147
315         jsr $ffd2               ; print clr
316 @L1:    lda #0                  ; restore view page
317         ldx #VDC_DSP_HI
318         jsr VDCWriteReg
319         lda OLDCOLOR
320         ldx #VDC_COLORS
321         jsr VDCWriteReg         ; restore color (background)
322         lda #$47
323         ldx #VDC_HSCROLL
324         jmp VDCWriteReg         ; switch to text screen
325
326 ; ------------------------------------------------------------------------
327 ; GETERROR: Return the error code in A and clear it.
328
329 GETERROR:
330         ldx     #TGI_ERR_OK
331         lda     ERROR
332         stx     ERROR
333         rts
334
335 ; ------------------------------------------------------------------------
336 ; CONTROL: Platform/driver specific entry point.
337 ;
338 ; Must set an error code: YES
339 ;
340
341 CONTROL:
342         lda     #TGI_ERR_INV_FUNC
343         sta     ERROR
344         rts
345
346 ; ------------------------------------------------------------------------
347 ; CLEAR: Clears the screen.
348 ;
349 ; Must set an error code: NO
350 ;
351
352 CLEAR:
353         lda     #0
354         ldy     SCRBASE
355         jsr     VDCSetSourceAddr
356         lda     #0
357         ldx     #VDC_VSCROLL
358         jsr     VDCWriteReg                     ; set fill mode
359         lda     #0
360         jsr     VDCWriteByte                    ; put 1rst byte (fill value)
361         ldy     #62                             ; 62 times
362         lda     #0                              ; 256 bytes
363         ldx     #VDC_COUNT
364 @L1:    jsr     VDCWriteReg
365         dey
366         bne     @L1
367         lda     #127
368         jmp     VDCWriteReg                     ; 1+62*256+127=16000=(640*256)/8
369
370 ; ------------------------------------------------------------------------
371 ; SETVIEWPAGE: Set the visible page. Called with the new page in A (0..n).
372 ; The page number is already checked to be valid by the graphics kernel.
373 ;
374 ; Must set an error code: NO (will only be called if page ok)
375 ;
376
377 SETVIEWPAGE:
378         clc
379         ror
380         ror
381         ror
382         ldx     #VDC_DSP_HI
383         jmp     VDCWriteReg
384
385 ; ------------------------------------------------------------------------
386 ; SETDRAWPAGE: Set the drawable page. Called with the new page in A (0..n).
387 ; The page number is already checked to be valid by the graphics kernel.
388 ;
389 ; Must set an error code: NO (will only be called if page ok)
390 ;
391
392 SETDRAWPAGE:
393         clc
394         ror
395         ror
396         ror
397         sta     SCRBASE
398         rts
399
400 ; ------------------------------------------------------------------------
401 ; SETCOLOR: Set the drawing color (in A). The new color is already checked
402 ; to be in a valid range (0..maxcolor-1).
403 ;
404 ; Must set an error code: NO (will only be called if color ok)
405 ;
406
407 SETCOLOR:
408         tax
409         beq     @L1
410         lda     #$FF
411 @L1:    sta     BITMASK
412         rts
413
414 ; ------------------------------------------------------------------------
415 ; SETPALETTE: Set the palette (not available with all drivers/hardware).
416 ; A pointer to the palette is passed in ptr1. Must set an error if palettes
417 ; are not supported
418 ;
419 ; Must set an error code: YES
420 ;
421
422 SETPALETTE:
423         ldy     #PALETTESIZE - 1
424 @L1:    lda     (ptr1),y        ; Copy the palette
425         and     #$0F            ; Make a valid color
426         sta     PALETTE,y
427         dey
428         bpl     @L1
429
430 ; Get the color entries from the palette
431
432         ldy     PALETTE+1       ; Foreground color
433         lda     COLTRANS,y
434         asl     a
435         asl     a
436         asl     a
437         asl     a
438         ldy     PALETTE         ; Background color
439         ora     COLTRANS,y
440
441         ldx     #VDC_COLORS
442         jsr     VDCWriteReg
443         lda     #TGI_ERR_OK     ; Clear error code
444         sta     ERROR
445         rts
446
447 ; ------------------------------------------------------------------------
448 ; GETPALETTE: Return the current palette in A/X. Even drivers that cannot
449 ; set the palette should return the default palette here, so there's no
450 ; way for this function to fail.
451 ;
452 ; Must set an error code: NO
453 ;
454
455 GETPALETTE:
456         lda     #<PALETTE
457         ldx     #>PALETTE
458         rts
459
460 ; ------------------------------------------------------------------------
461 ; GETDEFPALETTE: Return the default palette for the driver in A/X. All
462 ; drivers should return something reasonable here, even drivers that don't
463 ; support palettes, otherwise the caller has no way to determine the colors
464 ; of the (not changeable) palette.
465 ;
466 ; Must set an error code: NO (all drivers must have a default palette)
467 ;
468
469 GETDEFPALETTE:
470         lda     #<DEFPALETTE
471         ldx     #>DEFPALETTE
472         rts
473
474 ; ------------------------------------------------------------------------
475 ; SETPIXEL: Draw one pixel at X1/Y1 = ptr1/ptr2 with the current drawing
476 ; color. The coordinates passed to this function are never outside the
477 ; visible screen area, so there is no need for clipping inside this function.
478 ;
479 ; Must set an error code: NO
480 ;
481
482 SETPIXEL:
483         jsr     CALC            ; Calculate coordinates
484
485         stx     TEMP
486         lda     ADDR
487         ldy     ADDR+1
488         jsr     VDCSetSourceAddr
489         jsr     VDCReadByte
490         ldx     TEMP
491
492         sta     TEMP
493         eor     BITMASK
494         and     BITTAB,X
495         eor     TEMP
496         pha
497         lda     ADDR
498         ldy     ADDR+1
499         jsr     VDCSetSourceAddr
500         pla
501         jsr     VDCWriteByte
502
503 @L9:    rts
504
505 ; ------------------------------------------------------------------------
506 ; GETPIXEL: Read the color value of a pixel and return it in A/X. The
507 ; coordinates passed to this function are never outside the visible screen
508 ; area, so there is no need for clipping inside this function.
509
510
511 GETPIXEL:
512         jsr     CALC            ; Calculate coordinates
513
514         stx     TEMP            ; preserve X
515         lda     ADDR
516         ldy     ADDR+1
517         jsr     VDCSetSourceAddr
518         jsr     VDCReadByte
519         ldx     TEMP
520
521         ldy     #$00
522         and     BITTAB,X
523         beq     @L1
524         iny
525
526 @L1:    tya                     ; Get color value into A
527         ldx     #$00            ; Clear high byte
528         rts
529
530 ; ------------------------------------------------------------------------
531 ; BAR: Draw a filled rectangle with the corners X1/Y1, X2/Y2, where
532 ; X1/Y1 = ptr1/ptr2 and X2/Y2 = ptr3/ptr4 using the current drawing color.
533 ; Contrary to most other functions, the graphics kernel will sort and clip
534 ; the coordinates before calling the driver, so on entry the following
535 ; conditions are valid:
536 ;       X1 <= X2
537 ;       Y1 <= Y2
538 ;       (X1 >= 0) && (X1 < XRES)
539 ;       (X2 >= 0) && (X2 < XRES)
540 ;       (Y1 >= 0) && (Y1 < YRES)
541 ;       (Y2 >= 0) && (Y2 < YRES)
542 ;
543 ; Must set an error code: NO
544 ;
545
546 BAR:
547         inc     Y2
548         bne     HORLINE
549         inc     Y2+1
550
551 ; Original code for a horizontal line
552
553 HORLINE:
554         lda X1
555         pha
556         lda X1+1
557         pha
558         jsr CALC                ; get data for LEFT
559         lda BITMASKL,x          ; remember left address and bitmask
560         pha
561         lda ADDR
562         pha
563         lda ADDR+1
564         pha
565
566         lda X2
567         sta X1
568         lda X2+1
569         sta X1+1
570         jsr CALC                ; get data for RIGHT
571         lda BITMASKR,x
572         sta TEMP3
573
574         pla                     ; recall data for LEFT
575         sta X1+1
576         pla
577         sta X1                  ; put left address into X1
578         pla
579
580         cmp #%11111111          ; if left bit <> 0
581         beq @L1
582         sta TEMP2               ; do left byte only...
583         lda X1
584         ldy X1+1
585         jsr VDCSetSourceAddr
586         jsr VDCReadByte
587         sta TEMP
588         eor BITMASK
589         and TEMP2
590         eor TEMP
591         pha
592         lda X1
593         ldy X1+1
594         jsr VDCSetSourceAddr
595         pla
596         jsr VDCWriteByte
597         inc X1                  ; ... and proceed
598         bne @L1
599         inc X1+1
600
601         ; do right byte (if Y2=0 ++ADDR and skip)
602 @L1:    lda TEMP3
603         cmp #%11111111          ; if right bit <> 7
604         bne @L11
605         inc ADDR                ; right bit = 7 - the next one is the last
606         bne @L10
607         inc ADDR+1
608 @L10:   bne @L2
609
610 @L11:   lda ADDR                ; do right byte only...
611         ldy ADDR+1
612         jsr VDCSetSourceAddr
613         jsr VDCReadByte
614         sta TEMP
615         eor BITMASK
616         and TEMP3
617         eor TEMP
618         pha
619         lda ADDR
620         ldy ADDR+1
621         jsr VDCSetSourceAddr
622         pla
623         jsr VDCWriteByte
624
625 @L2:                            ; do the fill in the middle
626         lda ADDR                ; calculate offset in full bytes
627         sec
628         sbc X1
629         beq @L3                 ; if equal - there are no more bytes
630         sta ADDR
631
632         lda X1                  ; setup for the left side
633         ldy X1+1
634         jsr VDCSetSourceAddr
635         lda BITMASK             ; get color
636         jsr VDCWriteByte        ; put 1st value
637         ldx ADDR
638         dex
639         beq @L3                 ; 1 byte already written
640
641         stx ADDR                ; if there are more bytes - fill them...
642         ldx #VDC_VSCROLL
643         lda #0
644         jsr VDCWriteReg         ; setup for fill
645         ldx #VDC_COUNT
646         lda ADDR
647         jsr VDCWriteReg         ; ... fill them NOW!
648
649 @L3:    pla
650         sta X1+1
651         pla
652         sta X1
653
654 ; End of horizontal line code
655
656         inc     Y1
657         bne     @L4
658         inc     Y1+1
659 @L4:    lda     Y1
660         cmp     Y2
661         bne     @L5
662         lda     Y1+1
663         cmp     Y2+1
664         bne     @L5
665         rts
666
667 @L5:    jmp     HORLINE
668
669
670 ; ------------------------------------------------------------------------
671 ; TEXTSTYLE: Set the style used when calling OUTTEXT. Text scaling in X and Y
672 ; direction is passend in X/Y, the text direction is passed in A.
673 ;
674 ; Must set an error code: NO
675 ;
676
677 TEXTSTYLE:
678         stx     TEXTMAGX
679         sty     TEXTMAGY
680         sta     TEXTDIR
681         rts
682
683
684 ; ------------------------------------------------------------------------
685 ; OUTTEXT: Output text at X/Y = ptr1/ptr2 using the current color and the
686 ; current text style. The text to output is given as a zero terminated
687 ; string with address in ptr3.
688 ;
689 ; Must set an error code: NO
690 ;
691
692 OUTTEXT:
693         rts
694
695 ; ------------------------------------------------------------------------
696 ; Calculate all variables to plot the pixel at X1/Y1.
697 ;------------------------
698 ;< X1,Y1 - pixel
699 ;> ADDR - address of card
700 ;> X - bit number (X1 & 7)
701 CALC:
702         lda     Y1+1
703         sta     ADDR+1
704         lda     Y1
705         asl
706         rol     ADDR+1
707         asl
708         rol     ADDR+1          ; Y*4
709         clc
710         adc     Y1
711         sta     ADDR
712         lda     Y1+1
713         adc     ADDR+1
714         sta     ADDR+1          ; Y*4+Y=Y*5
715         lda     ADDR
716         asl
717         rol     ADDR+1
718         asl
719         rol     ADDR+1
720         asl
721         rol     ADDR+1
722         asl
723         rol     ADDR+1
724         sta     ADDR            ; Y*5*16=Y*80
725         lda     X1+1
726         sta     TEMP
727         lda     X1
728         lsr     TEMP
729         ror
730         lsr     TEMP
731         ror
732         lsr     TEMP
733         ror
734         clc
735         adc     ADDR
736         sta     ADDR
737         lda     ADDR+1          ; ADDR = Y*80+x/8
738         adc     TEMP
739         sta     ADDR+1
740         lda     ADDR+1
741         adc     SCRBASE
742         sta     ADDR+1
743         lda     X1
744         and     #7
745         tax
746         rts
747
748 ;-------------
749 ; VDC helpers
750
751 VDCSetSourceAddr:
752         pha
753         tya
754         ldx     #VDC_DATA_HI
755         jsr     VDCWriteReg
756         pla
757         ldx     #VDC_DATA_LO
758         bne     VDCWriteReg
759
760 VDCReadByte:
761         ldx     #VDC_DATA
762 VDCReadReg:
763         stx     VDC_ADDR_REG
764 @L0:    bit     VDC_ADDR_REG
765         bpl     @L0
766         lda     VDC_DATA_REG
767         rts
768
769 VDCWriteByte:
770         ldx     #VDC_DATA
771 VDCWriteReg:
772         stx     VDC_ADDR_REG
773 @L0:    bit     VDC_ADDR_REG
774         bpl     @L0
775         sta     VDC_DATA_REG
776         rts
777
778 ; ------------------------------------------------------------------------
779
780         .include        "../../tgi/tgidrv_line.inc"