From 4296cbaf82d47bfe1c9d9b100dcdf534601f018b Mon Sep 17 00:00:00 2001 From: Greg King Date: Wed, 15 Jul 2020 17:11:38 -0400 Subject: [PATCH] Added a 320x200x256 TGI driver to the Commander X16 library. Made the mandelbrot sample program handle the X16's 256 colors. --- doc/cx16.sgml | 37 +++- include/cx16.h | 24 +- libsrc/cx16/tgi/cx320p1.s | 401 ++++++++++++++++++++++++++++++++++ libsrc/cx16/tgi_stat_stddrv.s | 7 +- libsrc/cx16/tgi_stddrv.s | 6 +- samples/mandelbrot.c | 12 +- 6 files changed, 465 insertions(+), 22 deletions(-) create mode 100644 libsrc/cx16/tgi/cx320p1.s diff --git a/doc/cx16.sgml b/doc/cx16.sgml index 20094d609..a73bb3c1e 100644 --- a/doc/cx16.sgml +++ b/doc/cx16.sgml @@ -16,9 +16,12 @@ compiler. Overview

-The Commander X16 is a modern small computer with firmware that is based on -the ROMs in Commodore's VIC-20 and 64C. It has a couple of I/O chips -(WDC65C22 VIA) that are like the ones in the VIC-20. +The Commander X16 is a modern small computer with firmware that is based partly +on the ROMs in Commodore's VIC-20 and 64C. It has a couple of I/O chips +(WDC65C22 VIA) that are like the ones in the VIC-20. It supports file storage +on Secure Digital cards. It allows two joysticks and a mouse. It has three +sound devices. It's VGA screen has twice the range of the C64 (similar to the +C128's 80-column screen), with 256 colors. This file contains an overview of the CX16 run-time system as it comes with the cc65 C compiler. It describes the memory layout, CX16-specific header files, @@ -108,7 +111,7 @@ cl65 -o file.prg -t cx16 -C cx16-asm.cfg source.s To generate code that loads to $A000: -cl65 -o file.prg -Wl -S,$A000 -t cX16 -C cX16-asm.cfg source.s +cl65 -o file.prg -Wl -S,$A000 -t cx16 -C cx16-asm.cfg source.s It also is possible to add a small BASIC header to the program, that uses SYS @@ -208,12 +211,22 @@ structures, accessing the struct fields will access the chip registers. Loadable drivers

-The names in the parentheses denote the symbols to be used for static linking of the drivers. +The names in the parentheses denote the symbols to be used for static linking +of the drivers. The names fit into the 8.3 character limit of the SD-Card's +FAT32 file-system. Graphics drivers

-No graphics drivers are available currently for the CX16. +The default drivers, + + This driver features a resolution of 320 across and 200 down with 256 colors, + and a slightly adjustable palette (the order of the colors can be changed in + a way that's compatible with some of the other color drivers). +

Extended memory drivers

@@ -224,10 +237,10 @@ No extended memory drivers are available currently for the CX16. Joystick drivers

The default drivers, - + Supports up to two NES (and SNES) controllers connected to the joystick ports of the CX16. It reads the four directions, and the Mouse drivers

The default drivers, - + Supports a standard 3-button mouse connected to the PS/2 mouse port of the Commander X16. - Currently (r35), this driver doesn't support

diff --git a/include/cx16.h b/include/cx16.h index 253fa2482..f129b26f4 100644 --- a/include/cx16.h +++ b/include/cx16.h @@ -111,6 +111,25 @@ #define COLOR_LIGHTBLUE 0x0E #define COLOR_GRAY3 0x0F +/* TGI color defines */ +#define TGI_COLOR_BLACK COLOR_BLACK +#define TGI_COLOR_WHITE COLOR_WHITE +#define TGI_COLOR_RED COLOR_RED +#define TGI_COLOR_CYAN COLOR_CYAN +#define TGI_COLOR_VIOLET COLOR_VIOLET +#define TGI_COLOR_PURPLE COLOR_PURPLE +#define TGI_COLOR_GREEN COLOR_GREEN +#define TGI_COLOR_BLUE COLOR_BLUE +#define TGI_COLOR_YELLOW COLOR_YELLOW +#define TGI_COLOR_ORANGE COLOR_ORANGE +#define TGI_COLOR_BROWN COLOR_BROWN +#define TGI_COLOR_LIGHTRED COLOR_LIGHTRED +#define TGI_COLOR_GRAY1 COLOR_GRAY1 +#define TGI_COLOR_GRAY2 COLOR_GRAY2 +#define TGI_COLOR_LIGHTGREEN COLOR_LIGHTGREEN +#define TGI_COLOR_LIGHTBLUE COLOR_LIGHTBLUE +#define TGI_COLOR_GRAY3 COLOR_GRAY3 + /* NES controller masks for joy_read() */ #define JOY_BTN_1_MASK 0x80 @@ -280,8 +299,9 @@ struct __emul { /* The addresses of the static drivers */ -extern void cx16_std_joy[]; /* Referred to by joy_static_stddrv[] */ -extern void cx16_std_mou[]; /* Referred to by mouse_static_stddrv[] */ +extern void cx16_std_joy[]; /* Referenced by joy_static_stddrv[] */ +extern void cx16_std_mou[]; /* Referenced by mouse_static_stddrv[] */ +extern void cx320p1_tgi[]; /* Referenced by tgi_static_stddrv[] */ diff --git a/libsrc/cx16/tgi/cx320p1.s b/libsrc/cx16/tgi/cx320p1.s new file mode 100644 index 000000000..1ad5c2cbd --- /dev/null +++ b/libsrc/cx16/tgi/cx320p1.s @@ -0,0 +1,401 @@ +; +; Graphics driver for the 320 pixels across, 200 pixels down, 256 colors mode +; on the Commander X16 +; +; 2020-07-02, Greg King +; + + .include "zeropage.inc" + + .include "tgi-kernel.inc" + .include "tgi-error.inc" + + .include "cbm_kernal.inc" + .include "cx16.inc" + + .macpack generic + .macpack module + + +; Macro that copies a word into a pseudo-register + +.mac setReg reg, src + lda src + ldx src+1 + sta gREG::reg + stx gREG::reg+1 +.endmac + + +; ------------------------------------------------------------------------ +; Header. Includes jump table and constants. + + module_header _cx320p1_tgi ; 320 pixels across, 1 pixel per byte + +; First part of the header is a structure that has a signature, +; and defines the capabilities of the driver. + + .byte $74, $67, $69 ; ASCII "tgi" + .byte TGI_API_VERSION ; TGI API version number + .addr $0000 ; Library reference + .word 320 ; X resolution + .word 200 ; Y resolution + .byte <$0100 ; Number of drawing colors + .byte 1 ; Number of screens available + .byte 8 ; System font X size + .byte 8 ; System font Y size + .word $0100 ; Aspect ratio (based on VGA display) + .byte 0 ; TGI driver flags + +; Next, comes the jump table. Currently, all entries must be valid, +; and may point to an RTS for test versions (function not implemented). + + .addr INSTALL + .addr UNINSTALL + .addr INIT + .addr DONE + .addr GETERROR + .addr CONTROL + .addr CLEAR + .addr SETVIEWPAGE + .addr SETDRAWPAGE + .addr SETCOLOR + .addr SETPALETTE + .addr GETPALETTE + .addr GETDEFPALETTE + .addr SETPIXEL + .addr GETPIXEL + .addr LINE + .addr BAR + .addr TEXTSTYLE + .addr OUTTEXT + + +; ------------------------------------------------------------------------ +; Constant + +GRAPH320 = $80 + +; ------------------------------------------------------------------------ +; Data + +; Variables mapped to the zero-page segment variables. Some of these are +; used for passing parameters to the driver. + +X1 := ptr1 +Y1 := ptr2 +X2 := ptr3 +Y2 := ptr4 + +; Absolute variables used in the code + +.bss + +; The colors are indicies into a TGI palette. The TGI palette is indicies into +; VERA's palette. Vera's palette is a table of Red, Green, and Blue levels. +; The first 16 RGB elements mimic the Commodore 64's colors. + +defpalette: .res $0100 +palette: .res $0100 + +bcolor := palette + 0 ; Background color +color: .res 1 ; Stroke and fill index +mode: .res 1 ; Old text mode + +.data + +error: .byte TGI_ERR_OK ; Error code + + +.code + +; ------------------------------------------------------------------------ +; INSTALL routine. Is called after the driver is loaded into memory. May +; initialize anything that has to be done just once. Is probably empty +; most of the time. +; +; Must set an error code: NO + +INSTALL: +; Create the default palette. + + ldx #$00 +: txa + sta defpalette,x + inx + bnz :- + + ; Fall through. + +; ------------------------------------------------------------------------ +; UNINSTALL routine. Is called before the driver is removed from memory. May +; clean up anything done by INSTALL, but is probably empty most of the time. +; +; Must set an error code: NO + +UNINSTALL: + rts + +; ------------------------------------------------------------------------ +; INIT: Changes an already installed device from text mode to graphics +; mode. +; Note that INIT/DONE may be called multiple times while the driver +; is loaded, while INSTALL is called only once; so, any code that is needed +; to initiate variables and so on must go here. Setting the palette is not +; needed because that is called by the graphics kernel later. +; The graphics kernel never will call INIT when a graphics mode already is +; active, so there is no need to protect against that. +; +; Must set an error code: YES + +INIT: stz error ; #TGI_ERR_OK + +; Save the current text mode. + + lda SCREEN_MODE + sta mode + +; Switch into (320 x 200 x 256) graphics mode. + + lda #GRAPH320 + jmp SCREEN_SET_MODE + +; ------------------------------------------------------------------------ +; DONE: Will be called to switch the graphics device back into text mode. +; The graphics kernel never will call DONE when no graphics mode is active, +; so there is no need to protect against that. +; +; Must set an error code: NO + +DONE: +; Work around a prerelease 37 Kernal bug. +; VERA (graphics) layer 0 isn't disabled by SCREEN_SET_MODE. + + stz VERA::CTRL + lda VERA::DISP::VIDEO + and #<~VERA::DISP::ENABLE::LAYER0 + sta VERA::DISP::VIDEO + + lda mode + jmp SCREEN_SET_MODE + +; ------------------------------------------------------------------------ +; GETERROR: Return the error code in .A, and clear it. + +GETERROR: + lda error + stz error + rts + +; ------------------------------------------------------------------------ +; CONTROL: Platform-/driver-specific entry point. +; +; Must set an error code: YES + +CONTROL: + lda #TGI_ERR_INV_FUNC + sta error + rts + +; ------------------------------------------------------------------------ +; CLEAR: Clear the screen. +; +; Must set an error code: NO + +CLEAR := GRAPH_CLEAR + +; ------------------------------------------------------------------------ +; SETVIEWPAGE: Set the visible page. Called with the new page in .A (0..n-1). +; The page number already is checked to be valid by the graphics kernel. +; +; Must set an error code: NO (will be called only if page OK) + +SETVIEWPAGE: + + ; Fall through. + +; ------------------------------------------------------------------------ +; SETDRAWPAGE: Set the drawable page. Called with the new page in .A (0..n-1). +; The page number already is checked to be valid by the graphics kernel. +; +; Must set an error code: NO (will be called only if page OK) + +SETDRAWPAGE: + rts + +; ------------------------------------------------------------------------ +; SETPALETTE: Set the palette (not available with all drivers/hardware). +; A pointer to the palette is passed in ptr1. Must set an error if palettes +; are not supported +; +; Must set an error code: YES + +SETPALETTE: + stz error ; #TGI_ERR_OK + ldy #$00 +: lda (ptr1),y + sta palette,y + iny + bnz :- + + lda color ; Get stroke and fill index + + ; Fall through. + +; ------------------------------------------------------------------------ +; SETCOLOR: Set the drawing color (in .A). The new color already is checked +; to be in a valid range (0..maxcolor). +; +; Must set an error code: NO (will be called only if color OK) + +SETCOLOR: + tax + sta color + lda palette,x ; Set stroke and fill color + tax + ldy bcolor ; Get background color + jmp GRAPH_SET_COLORS + +; ------------------------------------------------------------------------ +; GETPALETTE: Return the current palette in .XA. Even drivers that cannot +; set the palette should return the default palette here, so there's no +; way for this function to fail. +; +; Must set an error code: NO + +GETPALETTE: + lda #palette + rts + +; ------------------------------------------------------------------------ +; GETDEFPALETTE: Return the default palette for the driver in .XA. All +; drivers should return something reasonable here, even drivers that don't +; support palettes, otherwise the caller has no way to determine the colors +; of the (not changable) palette. +; +; Must set an error code: NO (all drivers must have a default palette) + +GETDEFPALETTE: + lda #defpalette + rts + +; ------------------------------------------------------------------------ +; SETPIXEL: Draw one pixel at X1/Y1 = ptr1/ptr2 with the current drawing +; color. The co-ordinates passed to this function never are outside the +; visible screen area, so there is no need for clipping inside this function. +; +; Must set an error code: NO + +SETPIXEL: + jsr Point + jsr FB_CURSOR_POSITION + ldx color + lda palette,x + jmp FB_SET_PIXEL + +; ------------------------------------------------------------------------ +; GETPIXEL: Read the color value of a pixel, and return it in .XA. The +; co-ordinates passed to this function never are outside the visible screen +; area, so there is no need for clipping inside this function. + +GETPIXEL: + jsr Point + jsr FB_CURSOR_POSITION + jsr FB_GET_PIXEL + ldx #>$0000 + rts + +; ------------------------------------------------------------------------ +; LINE: Draw a line from X1/Y1 to X2/Y2, where X1/Y1 = ptr1/ptr2 and +; X2/Y2 = ptr3/ptr4, using the current drawing color. +; +; Must set an error code: NO + +LINE: jsr Point + setReg r2, X2 + setReg r3, Y2 + jmp GRAPH_DRAW_LINE + +; ------------------------------------------------------------------------ +; BAR: Draw a filled rectangle with the corners X1/Y1, X2/Y2, where +; X1/Y1 = ptr1/ptr2 and X2/Y2 = ptr3/ptr4, using the current drawing color. +; Contrary to most other functions, the graphics kernel will sort and clip +; the co-ordinates before calling the driver; so on entry, the following +; conditions are valid: +; X1 <= X2 +; Y1 <= Y2 +; (X1 >= 0) && (X1 < XRES) +; (X2 >= 0) && (X2 < XRES) +; (Y1 >= 0) && (Y1 < YRES) +; (Y2 >= 0) && (Y2 < YRES) +; +; Must set an error code: NO + +BAR: +; Set the starting corner. + + jsr Point + +; Set the width. + + lda X2 + sub X1 + sta gREG::r2 + lda X2+1 + sbc X1+1 + sta gREG::r2+1 + +; Set the height. + + lda Y2 + sub Y1 + sta gREG::r3 + lda Y2+1 + sbc Y1+1 + sta gREG::r3+1 + +; Set the corner radius. + + stz gREG::r4 + stz gREG::r4+1 + + sec ; Fill the rectangle + jmp GRAPH_DRAW_RECT + +; ------------------------------------------------------------------------ +; TEXTSTYLE: Set the style used when calling OUTTEXT. Text scaling in X and Y +; directions are passed in .X and .Y, the text direction is passed in .A. +; +; Must set an error code: NO + +TEXTSTYLE: + rts + +; ------------------------------------------------------------------------ +; OUTTEXT: Output text at X/Y = ptr1/ptr2 using the current color and the +; current text style. The text to output is given as a zero-terminated +; string with address in ptr3. +; +; Must set an error code: NO + +OUTTEXT: + jsr Point + + ldy #$00 +@next: lda (ptr3),y + bze @end + phy + jsr GRAPH_PUT_CHAR + ply + iny + bnz @next +@end: rts + +; ------------------------------------------------------------------------ +; Point: Set the arguments for the first point of a Kernal graphics function. + +Point: setReg r0, X1 + setReg r1, Y1 + rts diff --git a/libsrc/cx16/tgi_stat_stddrv.s b/libsrc/cx16/tgi_stat_stddrv.s index e501f4c2f..e3cec1c8c 100644 --- a/libsrc/cx16/tgi_stat_stddrv.s +++ b/libsrc/cx16/tgi_stat_stddrv.s @@ -1,10 +1,11 @@ ; ; Address of the static standard TGI driver ; -; 2019-12-22, Greg King +; 2020-06-04, Greg King ; ; const void tgi_static_stddrv[]; ; - .import _cx16_320x8b_tgi - .export _tgi_static_stddrv := _cx16_320x8b_tgi + .import _cx320p1_tgi ; 320 pixels across, 1 pixel per byte + + .export _tgi_static_stddrv := _cx320p1_tgi diff --git a/libsrc/cx16/tgi_stddrv.s b/libsrc/cx16/tgi_stddrv.s index 0e46a6cb3..f05ab7131 100644 --- a/libsrc/cx16/tgi_stddrv.s +++ b/libsrc/cx16/tgi_stddrv.s @@ -1,12 +1,14 @@ ; ; Name of the standard TGI driver ; -; 2019-12-22, Greg King +; 2020-06-04, Greg King ; ; const char tgi_stddrv[]; ; .export _tgi_stddrv +; A FAT32 8+3 file-name (for SD cards) + .rodata -_tgi_stddrv: .asciiz "cx16-320x8b.tgi" +_tgi_stddrv: .asciiz "cx320p1.tgi" diff --git a/samples/mandelbrot.c b/samples/mandelbrot.c index d7291c5b5..595bcfbbb 100644 --- a/samples/mandelbrot.c +++ b/samples/mandelbrot.c @@ -51,6 +51,7 @@ void mandelbrot (signed short x1, signed short y1, signed short x2, register signed short r, r1, i; register signed short xs, ys, xx, yy; register signed short x, y; + register unsigned char maxcol = MAXCOL; /* Calc stepwidth */ xs = ((x2 - x1) / (SCREEN_X)); @@ -76,10 +77,15 @@ void mandelbrot (signed short x1, signed short y1, signed short x2, if (count == maxiterations) { tgi_setcolor (0); } else { - if (MAXCOL == 2) { + switch (maxcol) { + case 2: tgi_setcolor (1); - } else { - tgi_setcolor (count % MAXCOL); + break; + case 0: /* 256 colors */ + tgi_setcolor (count); + break; + default: + tgi_setcolor (count % maxcol); } } /* Set pixel */