2 ; Graphics driver for the 160x102x16 mode on the Lynx.
4 ; All the drawing functions are simply done by sprites as the sprite
5 ; engine is the only way to do fast graphics on a Lynx.
7 ; So the code is not really based on any algorithms done by somebody.
8 ; I have looked at other routines in the cc65 libs to see what kind of
9 ; entry points we need. And I have looked at the old cc65 libs by
10 ; Bastian Schick to see how things worked in the past.
12 ; This code is written by Karri Kaksonen, 2004 for the cc65 compiler.
15 .include "zeropage.inc"
17 .include "tgi-kernel.inc"
18 .include "tgi-mode.inc"
19 .include "tgi-error.inc"
27 ; ------------------------------------------------------------------------
28 ; Header. Includes jump table and constants.
32 ; First part of the header is a structure that has a magic and defines the
33 ; capabilities of the driver
35 .byte $74, $67, $69 ; "tgi"
36 .byte TGI_API_VERSION ; TGI API version number
37 .word 160 ; X resolution
38 .word 102 ; Y resolution
39 .byte 16 ; Number of drawing colors
40 .byte 2 ; Number of screens available
41 .byte 8 ; System font X size
42 .byte 8 ; System font Y size
43 .res 4, $00 ; Reserved for future extensions
45 ; Next comes the jump table. All entries must be valid and may point to an RTS
46 ; for test versions (function not implemented).
68 .addr 0 ; IRQ entry is unused
70 ; ------------------------------------------------------------------------
73 ; Variables mapped to the zero page segment variables. Some of these are
74 ; used for passing parameters to the driver.
86 ; Absolute variables used in the code
90 ERROR: .res 1 ; Error code
92 DRAWINDEX: .res 1 ; Pen to use for drawing
102 BGINDEX: .res 1 ; Pen to use for text background
104 text_bitmap: .res 8*(1+20+1)+1
105 ; 8 rows with (one offset-byte plus 20 character bytes plus one fill-byte) plus one 0-offset-byte
107 ; Constants and tables
111 DEFPALETTE: .byte >$000
144 PALETTESIZE = * - DEFPALETTE
148 ; ------------------------------------------------------------------------
149 ; INSTALL routine. Is called after the driver is loaded into memory. May
150 ; initialize anything that has to be done just once. Is probably empty
153 ; Must set an error code: NO
164 ; ------------------------------------------------------------------------
165 ; UNINSTALL routine. Is called before the driver is removed from memory. May
166 ; clean up anything done by INSTALL but is probably empty most of the time.
168 ; Must set an error code: NO
175 ; ------------------------------------------------------------------------
176 ; INIT: Changes an already installed device from text mode to graphics
178 ; Note that INIT/DONE may be called multiple times while the driver
179 ; is loaded, while INSTALL is only called once, so any code that is needed
180 ; to initializes variables and so on must go here. Setting palette and
181 ; clearing the screen is not needed because this is called by the graphics
183 ; The graphics kernel will never call INIT when a graphics mode is already
184 ; active, so there is no need to protect against that.
186 ; Must set an error code: YES
190 ; Done, reset the error code
195 ; ------------------------------------------------------------------------
196 ; DONE: Will be called to switch the graphics device back into text mode.
197 ; The graphics kernel will never call DONE when no graphics mode is active,
198 ; so there is no need to protect against that.
200 ; Must set an error code: NO
206 ; ------------------------------------------------------------------------
207 ; GETERROR: Return the error code in A and clear it.
215 ; ------------------------------------------------------------------------
216 ; CONTROL: Platform/driver specific entry point.
218 ; Must set an error code: YES
220 ; The TGI lacks a way to draw sprites. As that functionality is vital to
221 ; Lynx games we borrow this CONTROL function to implement the still
222 ; missing tgi_draw_sprite funtion. To use this in your C-program
223 ; do a #define tgi_draw_sprite(spr) tgi_ioctl(0, spr)
225 ; To do a flip-screen call tgi_ioctl(1, 0)
227 ; To set the background index for text outputs call tgi_ioctl(2, bgindex)
267 lda ptr1 ; Get the sprite address
270 draw_sprite: ; Draw it in render buffer
289 ; ------------------------------------------------------------------------
290 ; CLEAR: Clears the screen.
292 ; Must set an error code: NO
297 .byte 3,%10000100,%00000000, $0 ; A pixel bitmap
299 .byte %00000001 ; A pixel sprite
310 CLEAR: lda #<cls_sprite
314 ; ------------------------------------------------------------------------
315 ; SETVIEWPAGE: Set the visible page. Called with the new page in A (0..n).
316 ; The page number is already checked to be valid by the graphics kernel.
318 ; Must set an error code: NO (will only be called if page ok)
320 ; It is a good idea to call this function during the vertical blanking
321 ; period. If you call it in the middle of the screen update then half of
322 ; the drawn frame will be from the old buffer and the other half is
323 ; from the new buffer. This is usually noticed by the user.
326 beq @L1 ; page == maxpages-1
334 sty VIEWPAGEL ; Save viewpage for getpixel
337 lda __viddma ; Process flipped displays
352 ; ------------------------------------------------------------------------
353 ; SETDRAWPAGE: Set the drawable page. Called with the new page in A (0..n).
354 ; The page number is already checked to be valid by the graphics kernel.
356 ; Must set an error code: NO (will only be called if page ok)
360 beq @L1 ; page == maxpages-1
372 ; ------------------------------------------------------------------------
373 ; SETCOLOR: Set the drawing color (in A). The new color is already checked
374 ; to be in a valid range (0..maxcolor-1).
376 ; Must set an error code: NO (will only be called if color ok)
383 ; ------------------------------------------------------------------------
384 ; SETPALETTE: Set the palette (not available with all drivers/hardware).
385 ; A pointer to the palette is passed in ptr1. Must set an error if palettes
388 ; Must set an error code: YES
394 sta GCOLMAP,y ; $FDA0
398 ; Done, reset the error code
404 ; ------------------------------------------------------------------------
405 ; GETPALETTE: Return the current palette in A/X. Even drivers that cannot
406 ; set the palette should return the default palette here, so there's no
407 ; way for this function to fail.
409 ; Must set an error code: NO
413 lda #<GCOLMAP ; $FDA0
417 ; ------------------------------------------------------------------------
418 ; GETDEFPALETTE: Return the default palette for the driver in A/X. All
419 ; drivers should return something reasonable here, even drivers that don't
420 ; support palettes, otherwise the caller has no way to determine the colors
421 ; of the (not changeable) palette.
423 ; Must set an error code: NO (all drivers must have a default palette)
431 ; ------------------------------------------------------------------------
432 ; SETPIXEL: Draw one pixel at X1/Y1 = ptr1/ptr2 with the current drawing
433 ; color. The coordinates passed to this function are never outside the
434 ; visible screen area, so there is no need for clipping inside this function.
436 ; Must set an error code: NO
441 .byte %00000001 ; A pixel sprite
463 ; ------------------------------------------------------------------------
464 ; GETPIXEL: Read the color value of a pixel and return it in A/X. The
465 ; coordinates passed to this function are never outside the visible screen
466 ; area, so there is no need for clipping inside this function.
471 sta MATHD ; Hardware multiply
502 ; ------------------------------------------------------------------------
503 ; LINE: Draw a line from X1/Y1 to X2/Y2, where X1/Y1 = ptr1/ptr2 and
504 ; X2/Y2 = ptr3/ptr4 using the current drawing color.
506 ; Must set an error code: NO
512 .byte 0 ; Will be replaced by the code
559 lda #%00000000 ; Not flipped
577 lda #%00010000 ; Vertical flip
592 sta MATHP ; hardware divide
605 bmi @L3 ; wait for math done (bit 7 of sprsys)
625 ; ------------------------------------------------------------------------
626 ; BAR: Draw a filled rectangle with the corners X1/Y1, X2/Y2, where
627 ; X1/Y1 = ptr1/ptr2 and X2/Y2 = ptr3/ptr4 using the current drawing color.
628 ; Contrary to most other functions, the graphics kernel will sort and clip
629 ; the coordinates before calling the driver, so on entry the following
630 ; conditions are valid:
633 ; (X1 >= 0) && (X1 < XRES)
634 ; (X2 >= 0) && (X2 < XRES)
635 ; (Y1 >= 0) && (Y1 < YRES)
636 ; (Y2 >= 0) && (Y2 < YRES)
638 ; Must set an error code: NO
643 .byte %00000001 ; A pixel sprite
674 ; ------------------------------------------------------------------------
675 ; CIRCLE: Draw a circle around the center X1/Y1 (= ptr1/ptr2) with the
676 ; radius in tmp1 and the current drawing color.
678 ; Must set an error code: NO
680 ; There is no sensible way of drawing a circle on a Lynx. As I would
681 ; have to use line elements to do the circle I rather do it in C than
682 ; create it here in the driver.
684 ; To do a circle please add this to your C program
688 ; 3196, // 11.25 degrees
689 ; 6270, // 22.5 degrees
690 ; 9102, // 33.75 degrees
691 ; 11585, // 45 degrees
692 ; 13623, // 56.25 degrees
693 ; 15137, // 67.5 degrees
694 ; 16069, // 78.75 degrees
695 ; 16384 // 90 degrees
712 ;void tgi_Circle(int x0, int y0, unsigned char r)
715 ; int x1, y1, x2, y2;
717 ; x1 = ((long)sin(0) * r + 8192) / 16384 + x0;
718 ; y1 = ((long)sin(8) * r + 8192) / 16384 + y0;
719 ; for (i = 1; i <= 32; i++) {
720 ; x2 = ((long)sin(i) * r + 8192) / 16384 + x0;
721 ; y2 = ((long)sin(i+8) * r + 8192) / 16384 + y0;
722 ; tgi_line(x1, y1, x2, y2);
728 ;#define tgi_circle tgi_Circle
733 ; ------------------------------------------------------------------------
734 ; TEXTSTYLE: Set the style used when calling OUTTEXT. Text scaling in X and Y
735 ; direction is passend in X/Y, the text direction is passed in A.
737 ; Must set an error code: NO
746 ; ------------------------------------------------------------------------
747 ; OUTTEXT: Output text at X/Y = ptr1/ptr2 using the current color and the
748 ; current text style. The text to output is given as a zero terminated
749 ; string with address in ptr3.
751 ; Must set an error code: NO
755 lda TEXTMAGX ; Scale sprite
760 stz text_sprite ; Set normal sprite
764 sta text_sprite ; Set opaque sprite
766 lda DRAWINDEX ; Set color
774 lda X1 ; Set start position
783 ldy #-1 ; Calculate string length
794 rts ; Zero length string
796 iny ; Prepare text_bitmap
800 ldy #8-1 ; 8 pixel lines per character
837 ; and now copy the 8 bytes of that char
843 ; draw char from top to bottom, reading char-data from offset 8-1 to offset 0
846 lda (FONTOFF),y ; *chptr
847 sta text_bitmap,x ;textbuf[y*(1+len+1)+1+x]
883 ; 96 characters from ASCII 32 to 127
884 ; 8 pixels wide, 8 pixels high
885 ; bit value 0 = foreground, bit value 1 = background / transparent
888 .byte $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF ;32
889 .byte $FF, $E7, $FF, $FF, $E7, $E7, $E7, $E7 ;33
890 .byte $FF, $FF, $FF, $FF, $FF, $99, $99, $99 ;34
891 .byte $FF, $99, $99, $00, $99, $00, $99, $99 ;35
892 .byte $FF, $E7, $83, $F9, $C3, $9F, $C1, $E7 ;36
893 .byte $FF, $B9, $99, $CF, $E7, $F3, $99, $9D ;37
894 .byte $FF, $C0, $99, $98, $C7, $C3, $99, $C3 ;38
895 .byte $FF, $FF, $FF, $FF, $FF, $E7, $F3, $F9 ;39
896 .byte $FF, $F3, $E7, $CF, $CF, $CF, $E7, $F3 ;40
897 .byte $FF, $CF, $E7, $F3, $F3, $F3, $E7, $CF ;41
898 .byte $FF, $FF, $99, $C3, $00, $C3, $99, $FF ;42
899 .byte $FF, $FF, $E7, $E7, $81, $E7, $E7, $FF ;43
900 .byte $CF, $E7, $E7, $FF, $FF, $FF, $FF, $FF ;44
901 .byte $FF, $FF, $FF, $FF, $81, $FF, $FF, $FF ;45
902 .byte $FF, $E7, $E7, $FF, $FF, $FF, $FF, $FF ;46
903 .byte $FF, $9F, $CF, $E7, $F3, $F9, $FC, $FF ;47
904 .byte $FF, $C3, $99, $99, $89, $91, $99, $C3 ;48
905 .byte $FF, $81, $E7, $E7, $E7, $C7, $E7, $E7 ;49
906 .byte $FF, $81, $9F, $CF, $F3, $F9, $99, $C3 ;50
907 .byte $FF, $C3, $99, $F9, $E3, $F9, $99, $C3 ;51
908 .byte $FF, $F9, $F9, $80, $99, $E1, $F1, $F9 ;52
909 .byte $FF, $C3, $99, $F9, $F9, $83, $9F, $81 ;53
910 .byte $FF, $C3, $99, $99, $83, $9F, $99, $C3 ;54
911 .byte $FF, $E7, $E7, $E7, $E7, $F3, $99, $81 ;55
912 .byte $FF, $C3, $99, $99, $C3, $99, $99, $C3 ;56
913 .byte $FF, $C3, $99, $F9, $C1, $99, $99, $C3 ;57
914 .byte $FF, $FF, $E7, $FF, $FF, $E7, $FF, $FF ;58
915 .byte $CF, $E7, $E7, $FF, $FF, $E7, $FF, $FF ;59
916 .byte $FF, $F1, $E7, $CF, $9F, $CF, $E7, $F1 ;60
917 .byte $FF, $FF, $FF, $81, $FF, $81, $FF, $FF ;61
918 .byte $FF, $8F, $E7, $F3, $F9, $F3, $E7, $8F ;62
919 .byte $FF, $E7, $FF, $E7, $F3, $F9, $99, $C3 ;63
922 .byte $FF, $C3, $9D, $9F, $91, $91, $99, $C3 ;0
923 .byte $FF, $99, $99, $99, $81, $99, $C3, $E7 ;1
924 .byte $FF, $83, $99, $99, $83, $99, $99, $83 ;2
925 .byte $FF, $C3, $99, $9F, $9F, $9F, $99, $C3 ;3
926 .byte $FF, $87, $93, $99, $99, $99, $93, $87 ;4
927 .byte $FF, $81, $9F, $9F, $87, $9F, $9F, $81 ;5
928 .byte $FF, $9F, $9F, $9F, $87, $9F, $9F, $81 ;6
929 .byte $FF, $C3, $99, $99, $91, $9F, $99, $C3 ;7
930 .byte $FF, $99, $99, $99, $81, $99, $99, $99 ;8
931 .byte $FF, $C3, $E7, $E7, $E7, $E7, $E7, $C3 ;9
932 .byte $FF, $C7, $93, $F3, $F3, $F3, $F3, $E1 ;10
933 .byte $FF, $99, $93, $87, $8F, $87, $93, $99 ;11
934 .byte $FF, $81, $9F, $9F, $9F, $9F, $9F, $9F ;12
935 .byte $FF, $9C, $9C, $9C, $94, $80, $88, $9C ;13
936 .byte $FF, $99, $99, $91, $81, $81, $89, $99 ;14
937 .byte $FF, $C3, $99, $99, $99, $99, $99, $C3 ;15
938 .byte $FF, $9F, $9F, $9F, $83, $99, $99, $83 ;16
939 .byte $FF, $F1, $C3, $99, $99, $99, $99, $C3 ;17
940 .byte $FF, $99, $93, $87, $83, $99, $99, $83 ;18
941 .byte $FF, $C3, $99, $F9, $C3, $9F, $99, $C3 ;19
942 .byte $FF, $E7, $E7, $E7, $E7, $E7, $E7, $81 ;20
943 .byte $FF, $C3, $99, $99, $99, $99, $99, $99 ;21
944 .byte $FF, $E7, $C3, $99, $99, $99, $99, $99 ;22
945 .byte $FF, $9C, $88, $80, $94, $9C, $9C, $9C ;23
946 .byte $FF, $99, $99, $C3, $E7, $C3, $99, $99 ;24
947 .byte $FF, $E7, $E7, $E7, $C3, $99, $99, $99 ;25
948 .byte $FF, $81, $9F, $CF, $E7, $F3, $F9, $81 ;26
949 .byte $FF, $C3, $CF, $CF, $CF, $CF, $CF, $C3 ;27
950 .byte $FF, $03, $9D, $CF, $83, $CF, $ED, $F3 ;28
951 .byte $FF, $C3, $F3, $F3, $F3, $F3, $F3, $C3 ;29
952 .byte $E7, $E7, $E7, $E7, $81, $C3, $E7, $FF ;30
953 .byte $FF, $EF, $CF, $80, $80, $CF, $EF, $FF ;31
957 .byte $FF, $C3, $9D, $9F, $91, $91, $99, $C3 ;224
958 .byte $FF, $C1, $99, $C1, $F9, $C3, $FF, $FF ;225
959 .byte $FF, $83, $99, $99, $83, $9F, $9F, $FF ;226
960 .byte $FF, $C3, $9F, $9F, $9F, $C3, $FF, $FF ;227
961 .byte $FF, $C1, $99, $99, $C1, $F9, $F9, $FF ;228
962 .byte $FF, $C3, $9F, $81, $99, $C3, $FF, $FF ;229
963 .byte $FF, $E7, $E7, $E7, $C1, $E7, $F1, $FF ;230
964 .byte $83, $F9, $C1, $99, $99, $C1, $FF, $FF ;231
965 .byte $FF, $99, $99, $99, $83, $9F, $9F, $FF ;232
966 .byte $FF, $C3, $E7, $E7, $C7, $FF, $E7, $FF ;233
967 .byte $C3, $F9, $F9, $F9, $F9, $FF, $F9, $FF ;234
968 .byte $FF, $99, $93, $87, $93, $9F, $9F, $FF ;235
969 .byte $FF, $C3, $E7, $E7, $E7, $E7, $C7, $FF ;236
970 .byte $FF, $9C, $94, $80, $80, $99, $FF, $FF ;237
971 .byte $FF, $99, $99, $99, $99, $83, $FF, $FF ;238
972 .byte $FF, $C3, $99, $99, $99, $C3, $FF, $FF ;239
973 .byte $9F, $9F, $83, $99, $99, $83, $FF, $FF ;240
974 .byte $F9, $F9, $C1, $99, $99, $C1, $FF, $FF ;241
975 .byte $FF, $9F, $9F, $9F, $99, $83, $FF, $FF ;242
976 .byte $FF, $83, $F9, $C3, $9F, $C1, $FF, $FF ;243
977 .byte $FF, $F1, $E7, $E7, $E7, $81, $E7, $FF ;244
978 .byte $FF, $C1, $99, $99, $99, $99, $FF, $FF ;245
979 .byte $FF, $E7, $C3, $99, $99, $99, $FF, $FF ;246
980 .byte $FF, $C9, $C1, $80, $94, $9C, $FF, $FF ;247
981 .byte $FF, $99, $C3, $E7, $C3, $99, $FF, $FF ;248
982 .byte $87, $F3, $C1, $99, $99, $99, $FF, $FF ;249
983 .byte $FF, $81, $CF, $E7, $F3, $81, $FF, $FF ;250
984 .byte $FF, $C3, $CF, $CF, $CF, $CF, $CF, $C3 ;251
985 .byte $FF, $03, $9D, $CF, $83, $CF, $ED, $F3 ;252
986 .byte $FF, $C3, $F3, $F3, $F3, $F3, $F3, $C3 ;253
987 .byte $E7, $E7, $E7, $E7, $81, $C3, $E7, $FF ;254
988 .byte $FF, $EF, $CF, $80, $80, $CF, $EF, $FF ;255