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 ; This code is written by Karri Kaksonen, 2004 for the cc65 compiler.
10 .include "zeropage.inc"
13 .include "tgi-kernel.inc"
14 .include "tgi-error.inc"
20 ; ------------------------------------------------------------------------
21 ; Header. Includes jump table and constants.
25 ; First part of the header is a structure that has a magic and defines the
26 ; capabilities of the driver
28 .byte $74, $67, $69 ; "tgi"
29 .byte TGI_API_VERSION ; TGI API version number
30 .word 160 ; X resolution
31 .word 102 ; Y resolution
32 .byte 16 ; Number of drawing colors
33 .byte 2 ; Number of screens available
34 .byte 8 ; System font X size
35 .byte 8 ; System font Y size
36 .word $0100 ; Aspect ratio (square pixel LCD)
37 .byte TGI_BM_FONT_FINESCALE ; TGI driver flags
39 ; Next comes the jump table. Currently all entries must be valid and may point
40 ; to an RTS for test versions (function not implemented). A future version may
41 ; allow for emulation: In this case the vector will be zero. Emulation means
42 ; that the graphics kernel will emulate the function by using lower level
43 ; primitives - for example ploting a line by using calls to SETPIXEL.
67 ; ------------------------------------------------------------------------
70 ; Variables mapped to the zero page segment variables. Some of these are
71 ; used for passing parameters to the driver.
83 ; Absolute variables used in the code
87 ERROR: .res 1 ; Error code
89 DRAWINDEX: .res 1 ; Pen to use for drawing
99 BGINDEX: .res 1 ; Pen to use for text background
101 ; Double buffer IRQ stuff
105 text_bitmap: .res 8*(1+20+1)+1
106 ; 8 rows with (one offset-byte plus 20 character bytes plus one fill-byte) plus one 0-offset-byte
108 ; Constants and tables
112 DEFPALETTE: .byte >$011
145 PALETTESIZE = * - DEFPALETTE
149 ; ------------------------------------------------------------------------
150 ; INSTALL routine. Is called after the driver is loaded into memory. May
151 ; initialize anything that has to be done just once. Is probably empty
154 ; Must set an error code: NO
167 ; ------------------------------------------------------------------------
168 ; UNINSTALL routine. Is called before the driver is removed from memory. May
169 ; clean up anything done by INSTALL but is probably empty most of the time.
171 ; Must set an error code: NO
178 ; ------------------------------------------------------------------------
179 ; INIT: Changes an already installed device from text mode to graphics
181 ; Note that INIT/DONE may be called multiple times while the driver
182 ; is loaded, while INSTALL is only called once, so any code that is needed
183 ; to initializes variables and so on must go here. Setting palette and
184 ; clearing the screen is not needed because this is called by the graphics
186 ; The graphics kernel will never call INIT when a graphics mode is already
187 ; active, so there is no need to protect against that.
189 ; Must set an error code: YES
193 ; Enable interrupts for VBL
196 ; Set up collision buffer to $A058
201 ; Put collision index before sprite data
206 ; Done, reset the error code
211 ; ------------------------------------------------------------------------
212 ; DONE: Will be called to switch the graphics device back into text mode.
213 ; The graphics kernel will never call DONE when no graphics mode is active,
214 ; so there is no need to protect against that.
216 ; Must set an error code: NO
222 ; ------------------------------------------------------------------------
223 ; GETERROR: Return the error code in A and clear it.
231 ; ------------------------------------------------------------------------
232 ; CONTROL: Platform/driver specific entry point.
234 ; Must set an error code: YES
236 ; The TGI lacks a way to draw sprites. As that functionality is vital to
237 ; Lynx games we borrow this CONTROL function to implement the still
238 ; missing tgi_draw_sprite funtion. To use this in your C-program
239 ; do a #define tgi_draw_sprite(spr) tgi_ioctl(0, spr)
241 ; To do a flip-screen call tgi_ioctl(1, 0)
243 ; To set the background index for text outputs call tgi_ioctl(2, bgindex)
245 ; To set the frame rate for the display hardware call tgi_ioctl(3, rate)
247 ; To check if the drawing engine is busy with the previous swap you can
248 ; call tgi_ioctl(4, 0). It returns 0 if idle and 1 if busy
250 ; To update displays you can call tgi_ioctl(4, 1) it will wait for the
251 ; next VBL interrupt and swap draw and view buffers.
253 ; Activate or deactivate collision detection by calling tgi_ioctl(5, 0/1).
256 pha ; Almost all control routines succeed
263 lda ptr1 ; Activate/deactivate collision detection
265 lda #%00000001 ; tgi_clear does not erase collision buffer
272 @L0: lda #%00000000 ; tgi_clear erases collision buffer
285 lda ptr1 ; Swap request
297 cmp #75 ; Set framerate
306 rate50: lda #$bd ; 50 Hz
309 rate60: lda #$9e ; 60 Hz
312 rate75: lda #$7e ; 75 Hz
321 bne ControlFlipScreen
323 lda ptr1 ; Set text background color
329 bne ControlDrawSprite
331 lda __sprsys ; Flip screen
356 lda ptr1 ; Get the sprite address
359 draw_sprite: ; Draw it in render buffer
378 ; ------------------------------------------------------------------------
379 ; CLEAR: Clears the screen.
381 ; Must set an error code: NO
386 .byte 3,%10000100,%00000000, $0 ; A pixel bitmap
391 .byte %00000001 ; A pixel sprite
402 CLEAR: lda #<cls_sprite
406 ; ------------------------------------------------------------------------
407 ; SETVIEWPAGE: Set the visible page. Called with the new page in A (0..n).
408 ; The page number is already checked to be valid by the graphics kernel.
410 ; Must set an error code: NO (will only be called if page ok)
412 ; It is a good idea to call this function during the vertical blanking
413 ; period. If you call it in the middle of the screen update then half of
414 ; the drawn frame will be from the old buffer and the other half is
415 ; from the new buffer. This is usually noticed by the user.
419 beq @L1 ; page == maxpages-1
427 sty VIEWPAGEL ; Save viewpage for getpixel
430 lda __viddma ; Process flipped displays
445 ; ------------------------------------------------------------------------
446 ; SETDRAWPAGE: Set the drawable page. Called with the new page in A (0..n).
447 ; The page number is already checked to be valid by the graphics kernel.
449 ; Must set an error code: NO (will only be called if page ok)
454 beq @L1 ; page == maxpages-1
466 ; ------------------------------------------------------------------------
467 ; IRQ: VBL interrupt handler
471 lda INTSET ; Poll all pending interrupts
473 beq IRQEND ; Exit if not a VBL interrupt
489 ; ------------------------------------------------------------------------
490 ; SETCOLOR: Set the drawing color (in A). The new color is already checked
491 ; to be in a valid range (0..maxcolor-1).
493 ; Must set an error code: NO (will only be called if color ok)
500 ; ------------------------------------------------------------------------
501 ; SETPALETTE: Set the palette (not available with all drivers/hardware).
502 ; A pointer to the palette is passed in ptr1. Must set an error if palettes
505 ; Must set an error code: YES
511 sta GCOLMAP,y ; $FDA0
515 ; Done, reset the error code
521 ; ------------------------------------------------------------------------
522 ; GETPALETTE: Return the current palette in A/X. Even drivers that cannot
523 ; set the palette should return the default palette here, so there's no
524 ; way for this function to fail.
526 ; Must set an error code: NO
530 lda #<GCOLMAP ; $FDA0
534 ; ------------------------------------------------------------------------
535 ; GETDEFPALETTE: Return the default palette for the driver in A/X. All
536 ; drivers should return something reasonable here, even drivers that don't
537 ; support palettes, otherwise the caller has no way to determine the colors
538 ; of the (not changeable) palette.
540 ; Must set an error code: NO (all drivers must have a default palette)
548 ; ------------------------------------------------------------------------
549 ; SETPIXEL: Draw one pixel at X1/Y1 = ptr1/ptr2 with the current drawing
550 ; color. The coordinates passed to this function are never outside the
551 ; visible screen area, so there is no need for clipping inside this function.
553 ; Must set an error code: NO
560 .byte %00000001 ; A pixel sprite
582 ; ------------------------------------------------------------------------
583 ; GETPIXEL: Read the color value of a pixel and return it in A/X. The
584 ; coordinates passed to this function are never outside the visible screen
585 ; area, so there is no need for clipping inside this function.
590 sta MATHD ; Hardware multiply
627 ; ------------------------------------------------------------------------
628 ; LINE: Draw a line from X1/Y1 to X2/Y2, where X1/Y1 = ptr1/ptr2 and
629 ; X2/Y2 = ptr3/ptr4 using the current drawing color.
631 ; Must set an error code: NO
638 .byte 0 ; Will be replaced by the code
686 lda #%00000000 ; Not flipped
704 lda #%00010000 ; Vertical flip
719 sta MATHP ; hardware divide
732 bmi @L3 ; wait for math done (bit 7 of sprsys)
752 ; ------------------------------------------------------------------------
753 ; BAR: Draw a filled rectangle with the corners X1/Y1, X2/Y2, where
754 ; X1/Y1 = ptr1/ptr2 and X2/Y2 = ptr3/ptr4 using the current drawing color.
755 ; Contrary to most other functions, the graphics kernel will sort and clip
756 ; the coordinates before calling the driver, so on entry the following
757 ; conditions are valid:
760 ; (X1 >= 0) && (X1 < XRES)
761 ; (X2 >= 0) && (X2 < XRES)
762 ; (Y1 >= 0) && (Y1 < YRES)
763 ; (Y2 >= 0) && (Y2 < YRES)
765 ; Must set an error code: NO
772 .byte %00000001 ; A pixel sprite
803 ; ------------------------------------------------------------------------
804 ; TEXTSTYLE: Set the style used when calling OUTTEXT. Text scaling in X and Y
805 ; direction is passend in X/Y, the text direction is passed in A.
807 ; Must set an error code: NO
816 ; ------------------------------------------------------------------------
817 ; OUTTEXT: Output text at X/Y = ptr1/ptr2 using the current color and the
818 ; current text style. The text to output is given as a zero terminated
819 ; string with address in ptr3.
821 ; Must set an error code: NO
825 lda TEXTMAGX ; Scale sprite
830 stz text_sprite ; Set normal sprite
834 sta text_sprite ; Set opaque sprite
836 lda DRAWINDEX ; Set color
844 lda X1 ; Set start position
853 ldy #<-1 ; Calculate string length
864 rts ; Zero length string
866 iny ; Prepare text_bitmap
870 ldy #8-1 ; 8 pixel lines per character
907 ; and now copy the 8 bytes of that char
913 ; draw char from top to bottom, reading char-data from offset 8-1 to offset 0
916 lda (FONTOFF),y ; *chptr
917 sta text_bitmap,x ;textbuf[y*(1+len+1)+1+x]
955 ; 96 characters from ASCII 32 to 127
956 ; 8 pixels wide, 8 pixels high
957 ; bit value 0 = foreground, bit value 1 = background / transparent
960 .byte $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF ;32
961 .byte $FF, $E7, $FF, $FF, $E7, $E7, $E7, $E7 ;33
962 .byte $FF, $FF, $FF, $FF, $FF, $99, $99, $99 ;34
963 .byte $FF, $99, $99, $00, $99, $00, $99, $99 ;35
964 .byte $FF, $E7, $83, $F9, $C3, $9F, $C1, $E7 ;36
965 .byte $FF, $B9, $99, $CF, $E7, $F3, $99, $9D ;37
966 .byte $FF, $C0, $99, $98, $C7, $C3, $99, $C3 ;38
967 .byte $FF, $FF, $FF, $FF, $FF, $E7, $F3, $F9 ;39
968 .byte $FF, $F3, $E7, $CF, $CF, $CF, $E7, $F3 ;40
969 .byte $FF, $CF, $E7, $F3, $F3, $F3, $E7, $CF ;41
970 .byte $FF, $FF, $99, $C3, $00, $C3, $99, $FF ;42
971 .byte $FF, $FF, $E7, $E7, $81, $E7, $E7, $FF ;43
972 .byte $CF, $E7, $E7, $FF, $FF, $FF, $FF, $FF ;44
973 .byte $FF, $FF, $FF, $FF, $81, $FF, $FF, $FF ;45
974 .byte $FF, $E7, $E7, $FF, $FF, $FF, $FF, $FF ;46
975 .byte $FF, $9F, $CF, $E7, $F3, $F9, $FC, $FF ;47
976 .byte $FF, $C3, $99, $99, $89, $91, $99, $C3 ;48
977 .byte $FF, $81, $E7, $E7, $E7, $C7, $E7, $E7 ;49
978 .byte $FF, $81, $9F, $CF, $F3, $F9, $99, $C3 ;50
979 .byte $FF, $C3, $99, $F9, $E3, $F9, $99, $C3 ;51
980 .byte $FF, $F9, $F9, $80, $99, $E1, $F1, $F9 ;52
981 .byte $FF, $C3, $99, $F9, $F9, $83, $9F, $81 ;53
982 .byte $FF, $C3, $99, $99, $83, $9F, $99, $C3 ;54
983 .byte $FF, $E7, $E7, $E7, $E7, $F3, $99, $81 ;55
984 .byte $FF, $C3, $99, $99, $C3, $99, $99, $C3 ;56
985 .byte $FF, $C3, $99, $F9, $C1, $99, $99, $C3 ;57
986 .byte $FF, $FF, $E7, $FF, $FF, $E7, $FF, $FF ;58
987 .byte $CF, $E7, $E7, $FF, $FF, $E7, $FF, $FF ;59
988 .byte $FF, $F1, $E7, $CF, $9F, $CF, $E7, $F1 ;60
989 .byte $FF, $FF, $FF, $81, $FF, $81, $FF, $FF ;61
990 .byte $FF, $8F, $E7, $F3, $F9, $F3, $E7, $8F ;62
991 .byte $FF, $E7, $FF, $E7, $F3, $F9, $99, $C3 ;63
994 .byte $FF, $C3, $9D, $9F, $91, $91, $99, $C3 ;0
995 .byte $FF, $99, $99, $99, $81, $99, $C3, $E7 ;1
996 .byte $FF, $83, $99, $99, $83, $99, $99, $83 ;2
997 .byte $FF, $C3, $99, $9F, $9F, $9F, $99, $C3 ;3
998 .byte $FF, $87, $93, $99, $99, $99, $93, $87 ;4
999 .byte $FF, $81, $9F, $9F, $87, $9F, $9F, $81 ;5
1000 .byte $FF, $9F, $9F, $9F, $87, $9F, $9F, $81 ;6
1001 .byte $FF, $C3, $99, $99, $91, $9F, $99, $C3 ;7
1002 .byte $FF, $99, $99, $99, $81, $99, $99, $99 ;8
1003 .byte $FF, $C3, $E7, $E7, $E7, $E7, $E7, $C3 ;9
1004 .byte $FF, $C7, $93, $F3, $F3, $F3, $F3, $E1 ;10
1005 .byte $FF, $99, $93, $87, $8F, $87, $93, $99 ;11
1006 .byte $FF, $81, $9F, $9F, $9F, $9F, $9F, $9F ;12
1007 .byte $FF, $9C, $9C, $9C, $94, $80, $88, $9C ;13
1008 .byte $FF, $99, $99, $91, $81, $81, $89, $99 ;14
1009 .byte $FF, $C3, $99, $99, $99, $99, $99, $C3 ;15
1010 .byte $FF, $9F, $9F, $9F, $83, $99, $99, $83 ;16
1011 .byte $FF, $F1, $C3, $99, $99, $99, $99, $C3 ;17
1012 .byte $FF, $99, $93, $87, $83, $99, $99, $83 ;18
1013 .byte $FF, $C3, $99, $F9, $C3, $9F, $99, $C3 ;19
1014 .byte $FF, $E7, $E7, $E7, $E7, $E7, $E7, $81 ;20
1015 .byte $FF, $C3, $99, $99, $99, $99, $99, $99 ;21
1016 .byte $FF, $E7, $C3, $99, $99, $99, $99, $99 ;22
1017 .byte $FF, $9C, $88, $80, $94, $9C, $9C, $9C ;23
1018 .byte $FF, $99, $99, $C3, $E7, $C3, $99, $99 ;24
1019 .byte $FF, $E7, $E7, $E7, $C3, $99, $99, $99 ;25
1020 .byte $FF, $81, $9F, $CF, $E7, $F3, $F9, $81 ;26
1021 .byte $FF, $C3, $CF, $CF, $CF, $CF, $CF, $C3 ;27
1022 .byte $FF, $03, $9D, $CF, $83, $CF, $ED, $F3 ;28
1023 .byte $FF, $C3, $F3, $F3, $F3, $F3, $F3, $C3 ;29
1024 .byte $E7, $E7, $E7, $E7, $81, $C3, $E7, $FF ;30
1025 .byte $FF, $EF, $CF, $80, $80, $CF, $EF, $FF ;31
1029 .byte $FF, $C3, $9D, $9F, $91, $91, $99, $C3 ;224
1030 .byte $FF, $C1, $99, $C1, $F9, $C3, $FF, $FF ;225
1031 .byte $FF, $83, $99, $99, $83, $9F, $9F, $FF ;226
1032 .byte $FF, $C3, $9F, $9F, $9F, $C3, $FF, $FF ;227
1033 .byte $FF, $C1, $99, $99, $C1, $F9, $F9, $FF ;228
1034 .byte $FF, $C3, $9F, $81, $99, $C3, $FF, $FF ;229
1035 .byte $FF, $E7, $E7, $E7, $C1, $E7, $F1, $FF ;230
1036 .byte $83, $F9, $C1, $99, $99, $C1, $FF, $FF ;231
1037 .byte $FF, $99, $99, $99, $83, $9F, $9F, $FF ;232
1038 .byte $FF, $C3, $E7, $E7, $C7, $FF, $E7, $FF ;233
1039 .byte $C3, $F9, $F9, $F9, $F9, $FF, $F9, $FF ;234
1040 .byte $FF, $99, $93, $87, $93, $9F, $9F, $FF ;235
1041 .byte $FF, $C3, $E7, $E7, $E7, $E7, $C7, $FF ;236
1042 .byte $FF, $9C, $94, $80, $80, $99, $FF, $FF ;237
1043 .byte $FF, $99, $99, $99, $99, $83, $FF, $FF ;238
1044 .byte $FF, $C3, $99, $99, $99, $C3, $FF, $FF ;239
1045 .byte $9F, $9F, $83, $99, $99, $83, $FF, $FF ;240
1046 .byte $F9, $F9, $C1, $99, $99, $C1, $FF, $FF ;241
1047 .byte $FF, $9F, $9F, $9F, $99, $83, $FF, $FF ;242
1048 .byte $FF, $83, $F9, $C3, $9F, $C1, $FF, $FF ;243
1049 .byte $FF, $F1, $E7, $E7, $E7, $81, $E7, $FF ;244
1050 .byte $FF, $C1, $99, $99, $99, $99, $FF, $FF ;245
1051 .byte $FF, $E7, $C3, $99, $99, $99, $FF, $FF ;246
1052 .byte $FF, $C9, $C1, $80, $94, $9C, $FF, $FF ;247
1053 .byte $FF, $99, $C3, $E7, $C3, $99, $FF, $FF ;248
1054 .byte $87, $F3, $C1, $99, $99, $99, $FF, $FF ;249
1055 .byte $FF, $81, $CF, $E7, $F3, $81, $FF, $FF ;250
1056 .byte $FF, $C3, $CF, $CF, $CF, $CF, $CF, $C3 ;251
1057 .byte $FF, $03, $9D, $CF, $83, $CF, $ED, $F3 ;252
1058 .byte $FF, $C3, $F3, $F3, $F3, $F3, $F3, $C3 ;253
1059 .byte $E7, $E7, $E7, $E7, $81, $C3, $E7, $FF ;254
1060 .byte $FF, $EF, $CF, $80, $80, $CF, $EF, $FF ;255