diff --git a/cfg/vic20-tgi.cfg b/cfg/vic20-tgi.cfg new file mode 100644 index 000000000..bc7845615 --- /dev/null +++ b/cfg/vic20-tgi.cfg @@ -0,0 +1,50 @@ +# Memory configuration which supports the "vic20-hi.tgi" driver. +# Memory configuration for a VIC-20 with, at least, 8K expansion RAM. +FEATURES { + STARTADDRESS: default = $1201; +} +SYMBOLS { + __LOADADDR__: type = import; + __EXEHDR__: type = import; + __TGIHDR__: type = import; + __STACKSIZE__: type = weak, value = $0200; # 512-byte stack + __HIMEM__: type = weak, value = $4000; +} +MEMORY { + ZP: file = "", define = yes, start = $0002, size = $001A; + LOADADDR: file = %O, start = %S - 2, size = $0002; + HEADER: file = %O, define = yes, start = %S, size = $003E; + MAIN: file = %O, define = yes, start = $2000, size = __HIMEM__ - __MAIN_START__ - __STACKSIZE__; +} +SEGMENTS { + ZEROPAGE: load = ZP, type = zp; + LOADADDR: load = LOADADDR, type = ro; +# The start-up code needs EXEHDR, TGI1HDR, TGI2HDR, +# and STARTUP to be next to each other, in that order. + EXEHDR: load = HEADER, type = ro; + TGI1HDR: load = HEADER, type = ro; + TGI2HDR: load = MAIN, type = ro; + STARTUP: load = MAIN, type = ro; + LOWCODE: load = MAIN, type = ro, optional = yes; + CODE: load = MAIN, type = ro; + RODATA: load = MAIN, type = ro; + DATA: load = MAIN, type = rw; + ONCE: load = MAIN, type = ro, optional = yes, define = yes; + INIT: load = MAIN, type = bss, optional = yes; + BSS: load = MAIN, type = bss, define = yes; +} +FEATURES { + CONDES: type = constructor, + label = __CONSTRUCTOR_TABLE__, + count = __CONSTRUCTOR_COUNT__, + segment = ONCE; + CONDES: type = destructor, + label = __DESTRUCTOR_TABLE__, + count = __DESTRUCTOR_COUNT__, + segment = RODATA; + CONDES: type = interruptor, + label = __INTERRUPTOR_TABLE__, + count = __INTERRUPTOR_COUNT__, + segment = RODATA, + import = __CALLIRQ__; +} diff --git a/doc/vic20.sgml b/doc/vic20.sgml index 4fcd0079c..0a6e85d4a 100644 --- a/doc/vic20.sgml +++ b/doc/vic20.sgml @@ -75,7 +75,6 @@ common to all CBM platforms. There are currently no special VIC20 functions. - CBM-specific functions

Some functions are available for all (or at least most) of the Commodore @@ -143,7 +142,33 @@ The names in the parentheses denote the symbols to be used for static linking of Graphics drivers

-No graphics drivers are currently available for the VIC20. + + + + This driver features a resolution of 160×192 with two colors. The + background can be chosen from a sixteen-color palette. The foreground can + be chosen from an eight-color palette. + + The driver will use memory from addresses $1000 to $1FFF as a graphics + buffer. Therefore, the VIC-20 must have, at least, 8K of expansion RAM. + + Programs that use this driver must be linked by the + cl65 -D DYN_DRV=0 -t vic20 -C vic20-tgi.cfg samples/mandelbrot.c + + + When the program starts, it will move itself up in RAM, to make room for the + buffer. When the program finishes, it will reset the BASIC interpreter. + That means that graphics pictures won't be preserved between the executions + of programs. Also, the graphics buffer shares RAM with the text screen. If + a picture must be saved, then a program must put it somewhere else (such as + a disk file) before returning to the text mode. + + Extended memory drivers

diff --git a/include/tgi.h b/include/tgi.h index 15a6437cc..135ef63f1 100644 --- a/include/tgi.h +++ b/include/tgi.h @@ -216,7 +216,7 @@ void __fastcall__ tgi_arc (int x, int y, unsigned char rx, unsigned char ry, /* Draw an ellipse arc with center at x/y and radii rx/ry using the current ** drawing color. The arc covers the angle between sa and ea (startangle and ** endangle), which must be in the range 0..360 (otherwise the function may -** bevave unextectedly). +** behave unexpectedly). */ void __fastcall__ tgi_pieslice (int x, int y, unsigned char rx, unsigned char ry, diff --git a/include/vic20.h b/include/vic20.h index 44512b3fd..d60c7e270 100644 --- a/include/vic20.h +++ b/include/vic20.h @@ -2,12 +2,12 @@ /* */ /* vic20.h */ /* */ -/* vic20 system specific definitions */ +/* VIC-20 system-specific definitions */ /* */ /* */ /* */ /* (C) 1998-2004 Ullrich von Bassewitz */ -/* Römerstraße 52 */ +/* Roemerstrasse 52 */ /* D-70794 Filderstadt */ /* EMail: uz@cc65.org */ /* */ @@ -66,14 +66,39 @@ #define COLOR_GREEN 0x05 #define COLOR_BLUE 0x06 #define COLOR_YELLOW 0x07 +/* Only the background and multi-color characters can have these colors. */ #define COLOR_ORANGE 0x08 -#define COLOR_BROWN 0x09 -#define COLOR_LIGHTRED 0x0A -#define COLOR_GRAY1 0x0B -#define COLOR_GRAY2 0x0C +#define COLOR_LIGHTORANGE 0x09 +#define COLOR_PINK 0x0A +#define COLOR_LIGHTCYAN 0x0B +#define COLOR_LIGHTVIOLET 0x0C #define COLOR_LIGHTGREEN 0x0D #define COLOR_LIGHTBLUE 0x0E -#define COLOR_GRAY3 0x0F +#define COLOR_LIGHTYELLOW 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_GREEN COLOR_GREEN +#define TGI_COLOR_BLUE COLOR_BLUE +#define TGI_COLOR_YELLOW COLOR_YELLOW +/* Only the background and multi-color graphics can have these colors. */ +#define TGI_COLOR_ORANGE COLOR_ORANGE +#define TGI_COLOR_LIGHTORANGE COLOR_LIGHTORANGE +#define TGI_COLOR_PINK COLOR_PINK +#define TGI_COLOR_LIGHTCYAN COLOR_LIGHTCYAN +#define TGI_COLOR_LIGHTVIOLET COLOR_LIGHTVIOLET +#define TGI_COLOR_LIGHTGREEN COLOR_LIGHTGREEN +#define TGI_COLOR_LIGHTBLUE COLOR_LIGHTBLUE +#define TGI_COLOR_LIGHTYELLOW COLOR_LIGHTYELLOW + + + +/* tgi_ioctl() commands */ +#define TGI_IOCTL_VIC20_SET_PATTERN 0x01 /* Set 8-byte pattern for tgi_bar(). */ @@ -108,5 +133,9 @@ extern void vic20_stdjoy_joy[]; /* Referred to by joy_static_stddrv[] */ extern void vic20_rama_emd[]; extern void vic20_georam_emd[]; +extern void vic20_hi_tgi[]; /* Referred to by tgi_static_stddrv[] */ + + + /* End of vic20.h */ #endif diff --git a/libsrc/vic20/libref.s b/libsrc/vic20/libref.s index 468c540f1..333a66894 100644 --- a/libsrc/vic20/libref.s +++ b/libsrc/vic20/libref.s @@ -1,10 +1,14 @@ ; -; Oliver Schmidt, 2013-05-31 +; 2013-05-31, Oliver Schmidt +; 2018-03-11, Sven Michael Klose ; - .export joy_libref .export em_libref + .export joy_libref + .export tgi_libref + .import _exit -joy_libref := _exit em_libref := _exit +joy_libref := _exit +tgi_libref := _exit diff --git a/libsrc/vic20/tgi/vic20-hi.s b/libsrc/vic20/tgi/vic20-hi.s new file mode 100644 index 000000000..8dfa46d6b --- /dev/null +++ b/libsrc/vic20/tgi/vic20-hi.s @@ -0,0 +1,1018 @@ +; +; Graphics driver for a 160x192x2 mode on the VIC-20. +; +; Based on C64 TGI driver +; +; 2018-03-11, Sven Michael Klose +; 2020-07-06, Greg King +; + + .include "zeropage.inc" + .include "vic20.inc" + + .include "tgi-kernel.inc" + .include "tgi-error.inc" + + .macpack generic + .macpack module + + +; ------------------------------------------------------------------------ +; Constants + +COLS = 20 +ROWS = 12 +XRES = COLS * 8 +YRES = ROWS * 16 + +TGI_IOCTL_VIC20_SET_PATTERN = $01 + +; ------------------------------------------------------------------------ +; Header. Includes jump table and constants. + + module_header _vic20_hi_tgi + +; First part of the header is a structure that has a magic 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 XRES ; X resolution + .word YRES ; Y resolution + .byte 2 ; Number of drawing colors + .byte 1 ; Number of screens available + .byte 8 ; System font X size + .byte 8 ; System font Y size + .word $0180 ; Aspect ratio 2.5:3 + .byte 0 ; TGI driver flags + +; Next comes the jump table. With the exception of IRQ, 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 + +; ------------------------------------------------------------------------ +; 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 +TEXT := ptr3 + +POINT := regsave +SOURCE := tmp1 +DEST := tmp3 + +; Absolute variables used in the code + +.bss + +ERROR: .res 1 ; Error code +PALETTE: .res 2 ; The current palette + +CURCOL: .res 1 ; Current color. +BITMASK: .res 1 ; $00 = clear, $FF = set pixels + +; BAR variables + +XPOSR: .res 1 ; Used by BAR. +PATTERN: .res 2 ; Address of pattern. +USERPATTERN: .res 2 ; User-defined pattern set via CONTROL. +COUNTER: .res 2 +TMP: .res 1 +MASKS: .res 1 +MASKD: .res 1 +XCPOS: .res 1 +HEIGHT: .res 1 + +; Line variables + +CHUNK := X2 ; Used in the line routine +OLDCHUNK := X2+1 ; Ditto +TEMP := tmp4 +TEMP2 := sreg +DX: .res 2 +DY: .res 2 + +; Text output stuff + +TEXTMAGX: .res 1 +TEXTMAGY: .res 1 +TEXTDIR: .res 1 + +; Constants and tables + +.rodata + +DEFPALETTE: .byte $00, $01 ; White on black +PALETTESIZE = * - DEFPALETTE + +BITTAB: .byte $80, $40, $20, $10, $08, $04, $02, $01 +BITCHUNK: .byte $FF, $7F, $3F, $1F, $0F, $07, $03, $01 + +CHARROM := $8000 ; Character ROM base address +CBASE := $9400 ; Color memory base address +SBASE := $1000 ; Screen memory base address +VBASE := $1100 ; Video memory base address + +; These numbers are added to Kernal's default VIC settings. +; They make the Video Interface Controller show graphics instead of text. + +.proc VICREGS + .byte <+2 ; Left_edge + 2 + .byte <-2 ; Top_edge - 2 + .byte <-2 ; Columns - 2 + .byte <-(11 << 1) + 1 ; Rows - 11, chars. are 8 x 16 pixels + .byte 0 + .byte <+$0C ; Font_address = $1000 +.endproc + +XADDRS_L: + .repeat COLS, n + .byte <(VBASE + YRES * n) + .endrep + +XADDRS_H: + .repeat COLS, n + .byte >(VBASE + YRES * n) + .endrep + +MASKS_LEFT: + .byte %11111111 +MASKD_RIGHT: + .byte %01111111 + .byte %00111111 + .byte %00011111 + .byte %00001111 + .byte %00000111 + .byte %00000011 + .byte %00000001 +MASKD_LEFT: + .byte %00000000 +MASKS_RIGHT: + .byte %10000000 + .byte %11000000 + .byte %11100000 + .byte %11110000 + .byte %11111000 + .byte %11111100 + .byte %11111110 + .byte %11111111 + +PATTERN_EMPTY: + .byte %00000000 + .byte %00000000 + .byte %00000000 + .byte %00000000 + .byte %00000000 + .byte %00000000 + .byte %00000000 + .byte %00000000 + +PATTERN_SOLID: + .byte %11111111 + .byte %11111111 + .byte %11111111 + .byte %11111111 + .byte %11111111 + .byte %11111111 + .byte %11111111 + .byte %11111111 + +.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 +; + +.proc INSTALL + +; Reset user-defined pattern. + + lda #$00 + sta PATTERN + sta PATTERN+1 + rts +.endproc + +; ------------------------------------------------------------------------ +; 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 +; + +.proc UNINSTALL + rts +.endproc + +; ------------------------------------------------------------------------ +; 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 initialize variables and so on must go here. Setting the palette +; is not needed because that is done 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 +; + +.proc INIT + +; Initialize variables + + ldx #$FF + stx BITMASK + +; Make screen columns. + + lda #SBASE + sta tmp2+1 + inx ; (ldx #$00) + stx ERROR ; Set to TGI_ERR_OK + +@NEXT_ROW: + ldy #$00 + txa + clc + adc #$10 + +@NEXT_COLUMN: + sta (tmp2),y + clc + adc #ROWS + iny + cpy #COLS + bne @NEXT_COLUMN + +; Step to next row on screen. + + lda tmp2 + clc + adc #COLS + sta tmp2 + bcc @L1 + inc tmp2+1 + +@L1: inx + cpx #ROWS + bne @NEXT_ROW + +; Set up VIC. + + ldx #.sizeof(VICREGS) - 1 +@L2: clc + lda $EDE4,x + adc VICREGS,x + sta VIC,x + dex + bpl @L2 + + jmp CLEAR +.endproc + +; ------------------------------------------------------------------------ +; 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 +; + +.proc DONE + jmp $E518 ; Kernal console init. +.endproc + +; ------------------------------------------------------------------------ +; GETERROR: Return the error code in A, and clear it. + +.proc GETERROR + ldx #TGI_ERR_OK + lda ERROR + stx ERROR + rts +.endproc + +; ------------------------------------------------------------------------ +; CONTROL: Platform-/driver-specific entry point. +; +; Must set an error code: YES +; + +.proc CONTROL + +; Set user-defined pattern. + + cmp #TGI_IOCTL_VIC20_SET_PATTERN + bne @INVALID_FUNC + + lda ptr1 + sta USERPATTERN + lda ptr1+1 + sta USERPATTERN+1 + + lda #TGI_ERR_OK + .byte $2C ;(bit $1234) + +; Return with error code for invalid function index. + +@INVALID_FUNC: + lda #TGI_ERR_INV_FUNC + sta ERROR + rts +.endproc + +; ------------------------------------------------------------------------ +; CLEAR: Clears the screen. All pixels are set to the background color. +; +; Must set an error code: NO +; + +.proc CLEAR + lda #%00000000 + tay ; (ldy #$00) +@L1: sta VBASE + $0000,y + sta VBASE + $0100,y + sta VBASE + $0200,y + sta VBASE + $0300,y + sta VBASE + $0400,y + sta VBASE + $0500,y + sta VBASE + $0600,y + sta VBASE + $0700,y + sta VBASE + $0800,y + sta VBASE + $0900,y + sta VBASE + $0A00,y + sta VBASE + $0B00,y + sta VBASE + $0C00,y + sta VBASE + $0D00,y + sta VBASE + $0E00,y + iny + bne @L1 + rts +.endproc + +; ------------------------------------------------------------------------ +; SETVIEWPAGE: Set the visible page. Called with the new page in .A (0..n). +; 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) +; + +.proc SETVIEWPAGE + rts +.endproc + +; ------------------------------------------------------------------------ +; 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) +; + +.proc SETDRAWPAGE + rts +.endproc + +; ------------------------------------------------------------------------ +; 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) +; + +.proc SETCOLOR + sta CURCOL + tax + beq @L1 + lda #$FF +@L1: sta BITMASK + rts +.endproc + +; ------------------------------------------------------------------------ +; 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 +; + +.proc SETPALETTE + ldy #PALETTESIZE - 1 +@L1: lda (ptr1),y ; Copy the palette + and #$0F ; Make a valid color + sta PALETTE,y + dey + bpl @L1 + +; Initialize the color map with the new color settings. + + iny ; Set .Y to $00 + lda PALETTE+1 ; Foreground color +@L2: sta CBASE + $0000,y + sta CBASE + $0100,y + iny + bne @L2 + + lda PALETTE ; Background color + asl a ; Put it in high nybble + asl a + asl a + asl a + sta tmp2 + lda VIC_COLOR + and #$0F + ora tmp2 + sta VIC_COLOR + +; Done, reset the error code + + ;ldy #TGI_ERR_OK ; (Already zero) + sty ERROR + rts +.endproc + +; ------------------------------------------------------------------------ +; 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 +; + +.proc GETPALETTE + lda #PALETTE + rts +.endproc + +; ------------------------------------------------------------------------ +; 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 changeable) palette. +; +; Must set an error code: NO (all drivers must have a default palette) +; + +.proc GETDEFPALETTE + lda #DEFPALETTE + rts +.endproc + +; ------------------------------------------------------------------------ +; 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's no need for clipping inside this function. +; +; Must set an error code: NO +; + +.proc SETPIXEL + jsr CALC ; Calculate co-ordinates + + ldy #$00 + lda (POINT),Y + eor BITMASK + and BITTAB,X + eor (POINT),Y + sta (POINT),Y + rts +.endproc + +; ------------------------------------------------------------------------ +; 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's no need for clipping inside this function. + + +.proc GETPIXEL + jsr CALC ; Calculate co-ordinates + + ldy #$00 + lda (POINT),Y + and BITTAB,X + beq @L1 + iny + tya ; Get color value into .A +@L1: ldx #>$0000 ; Clear high byte + rts +.endproc + +; ------------------------------------------------------------------------ +; TEXTSTYLE: Set the style used when calling OUTTEXT. Text scalings in x and y +; directions are passed in .X/.Y; the text direction is passed in .A. +; +; Must set an error code: NO +; + +.proc TEXTSTYLE + stx TEXTMAGX + sty TEXTMAGY + sta TEXTDIR + rts +.endproc + +; ------------------------------------------------------------------------ +; 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 +; + +.proc OUTTEXT + rts +.endproc + +; ------------------------------------------------------------------------ +; Calculate address and X offset in char. line, to plot the pixel at X1/Y1. + +.proc CALC + lda X1+1 + bne @L1 + lda Y1+1 + bne @L1 + + lda X1 + lsr + lsr + lsr + tay + + lda XADDRS_L,y + clc + adc Y1 + sta POINT + lda XADDRS_H,y + adc #$00 + sta POINT+1 + + lda X1 + and #7 + tax + +@L1: rts +.endproc + +; ------------------------------------------------------------------------ +; 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. +; +; X1,X2 etc. are set up above (x2=LINNUM in particular) +; Format is LINE x2,y2,x1,y1 +; +; Must set an error code: NO +; + +.proc LINE + +@CHECK: lda X2 ; Make sure x1 < x2 + sec + sbc X1 + tax + lda X2+1 + sbc X1+1 + bpl @CONT + lda Y2 ; If not, swap P1 and P2 + ldy Y1 + sta Y1 + sty Y2 + lda Y2+1 + ldy Y1+1 + sta Y1+1 + sty Y2+1 + lda X1 + ldy X2 + sty X1 + sta X2 + lda X2+1 + ldy X1+1 + sta X1+1 + sty X2+1 + bcc @CHECK + +@CONT: sta DX+1 + stx DX + + ldx #$C8 ; INY + lda Y2 ; Calculate dy + sec + sbc Y1 + tay + lda Y2+1 + sbc Y1+1 + bpl @DYPOS ; Is y2 >= y1? + lda Y1 ; Otherwise, dy = y1 - y2 + sec + sbc Y2 + tay + ldx #$88 ; DEY + +@DYPOS: sty DY ; 8-bit DY -- FIX ME? + stx YINCDEC + stx XINCDEC + + lda X1 + lsr + lsr + lsr + tay + lda XADDRS_L,y + sta POINT + lda XADDRS_H,y + sta POINT+1 + ldy Y1 + + lda X1 + and #7 + tax + lda BITCHUNK,X + sta OLDCHUNK + sta CHUNK + + ldx DY + cpx DX ; Who's bigger: dy or dx? + bcc STEPINX ; If dx, then... + lda DX+1 + bne STEPINX + +; +; Big steps in Y +; +; X is now counter, Y is y co-ordinate +; +; On entry, X=DY=number of loop iterations, and Y=Y1 +STEPINY: + lda #$00 + sta OLDCHUNK ; So plotting routine will work right + lda CHUNK + lsr ; Strip the bit + eor CHUNK + sta CHUNK + txa + bne @CONT ; If dy=0, it's just a point + inx +@CONT: lsr ; Init. counter to dy/2 +; +; Main loop +; +YLOOP: sta TEMP + + lda (POINT),y ; Plot + eor BITMASK + and CHUNK + eor (POINT),y + sta (POINT),y +YINCDEC: + iny ; Advance Y co-ordinate + lda TEMP ; Restore A + sec + sbc DX + bcc YFIXX +YCONT: dex ; X is counter + bne YLOOP +YCONT2: lda (POINT),y ; Plot endpoint + eor BITMASK + and CHUNK + eor (POINT),y + sta (POINT),y + rts + +YFIXX: ; x=x+1 + adc DY + lsr CHUNK + bne YCONT ; If we pass a column boundary, + ror CHUNK ; then reset CHUNK to $80 + sta TEMP2 + lda POINT + adc #YRES + sta POINT + bcc @CONT + inc POINT+1 +@CONT: lda TEMP2 + dex + bne YLOOP + beq YCONT2 + +; +; Big steps in X direction +; +; On entry, X=DY=number of loop iterations, and Y=Y1 + +.bss +COUNTHI: + .byte $00 ; Temporary counter, + ; used only once +.code +STEPINX: + ldx DX + lda DX+1 + sta COUNTHI + cmp #$80 + ror ; Need bit for initialization + sta Y1 ; High byte of counter + txa + bne @CONT ; Could be $100 + dec COUNTHI +@CONT: ror +; +; Main loop +; +XLOOP: lsr CHUNK + beq XFIXC ; If we pass a column boundary... +XCONT1: sbc DY + bcc XFIXY ; Time to step in Y? +XCONT2: dex + bne XLOOP + dec COUNTHI ; High bits set? + bpl XLOOP + + lsr CHUNK ; Advance to last point + jmp LINEPLOT ; Plot the last chunk +; +; CHUNK has passed a column; so, plot and increment pointer; +; and fix up CHUNK, OLDCHUNK. +; +XFIXC: sta TEMP + jsr LINEPLOT + lda #$FF + sta CHUNK + sta OLDCHUNK + lda POINT + clc + adc #YRES + sta POINT + lda TEMP + bcc XCONT1 + inc POINT+1 + jmp XCONT1 +; +; Check to make sure there isn't a high bit; plot chunk; +; and update Y co-ordinate. +; +XFIXY: dec Y1 ; Maybe high bit set + bpl XCONT2 + adc DX + sta TEMP + lda DX+1 + adc #$FF ; Hi byte + sta Y1 + + jsr LINEPLOT ; Plot chunk + lda CHUNK + sta OLDCHUNK + + lda TEMP +XINCDEC: + iny ; Y co-ord + jmp XCONT2 + +; +; Subroutine to plot chunks/points (to save a little +; room, gray hair, etc.) +; +LINEPLOT: ; Plot the line chunk + lda (POINT),Y + eor BITMASK + ora CHUNK + and OLDCHUNK + eor CHUNK + eor (POINT),Y + sta (POINT),Y + rts +.endproc + +; In: xpos, ypos, width, height +; ------------------------------------------------------------------------ +; 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 +; + +.proc BAR + +; Set user pattern if available. + + lda USERPATTERN + ora USERPATTERN+1 + beq @GET_PATTERN_BY_COLOR + + lda USERPATTERN + sta PATTERN + lda USERPATTERN+1 + sta PATTERN+1 + jmp @GOT_PATTERN + +; Determine pattern based on current colour. + +@GET_PATTERN_BY_COLOR: + lda #PATTERN_SOLID + ldy CURCOL + bne @L2 + lda #PATTERN_EMPTY +@L2: sta PATTERN + stx PATTERN+1 + +@GOT_PATTERN: + +; Get starting POINT on screen. + + jsr CALC + sty XCPOS + lda POINT ; One off for VFILL/VCOPY. + sec + sbc #1 + sta POINT + bcs @L3 + dec POINT+1 +@L3: + +; Get height for VFILL. + + lda Y2 + sec + sbc Y1 + sta HEIGHT + +; Get rightmost char column. + + lda X2 + and #7 + sta XPOSR + +; Get width in characters. + + lda X2 + lsr + lsr + lsr + sec + sbc XCPOS + beq @DRAW_SINGLE_COLUMN + sta COUNTER + +; Draw left end. + + lda X1 + and #7 + tax + lda MASKD_LEFT,x + sta MASKD + lda MASKS_LEFT,x + sta MASKS + jsr VFILL + jsr INCPOINTX + +; Draw middle. + + dec COUNTER + beq @DRAW_RIGHT_END +@L1: jsr VCOPY + jsr INCPOINTX + dec COUNTER + bne @L1 + +; Draw right end. + +@DRAW_RIGHT_END: + ldx XPOSR + lda MASKD_RIGHT,x + sta MASKD + lda MASKS_RIGHT,x + sta MASKS + jmp VFILL + +; Draw left end. + +@DRAW_SINGLE_COLUMN: + lda X1 + and #7 + tax + ldy XPOSR + lda MASKS_LEFT,x + and MASKS_RIGHT,y + sta MASKS + lda MASKD_LEFT,x + ora MASKD_RIGHT,y + sta MASKD + jmp VFILL +.endproc + +; In: HEIGHT, PATTERN +; MASKS: Source mask (ANDed with pattern). +; MASKD: Destination mask (ANDed with screen). +; POINT: Starting address. +; ------------------------------------------------------------------------ +; Fill column with pattern using masks. +; + +.proc VFILL + lda PATTERN + sta @MOD_PATTERN+1 + lda PATTERN+1 + sta @MOD_PATTERN+2 + ldy HEIGHT + lda Y1 + and #7 + tax + +@L1: lda (POINT),y + and MASKD + sta TMP +@MOD_PATTERN: + lda $FFFF,x + and MASKS + ora TMP + sta (POINT),y + inx + txa + and #7 + tax + dey + bne @L1 + + rts +.endproc + +; In: HEIGHT, PATTERN, POINT +; ------------------------------------------------------------------------ +; Fill column with pattern. +; + +.proc VCOPY + lda PATTERN + sta @MOD_PATTERN+1 + lda PATTERN+1 + sta @MOD_PATTERN+2 + ldy HEIGHT + lda Y1 + and #7 + tax + +@MOD_PATTERN: +@L1: lda $FFFF,x + sta (POINT),y + inx + txa + and #7 + tax + dey + bne @L1 + + rts +.endproc + +.proc INCPOINTX + lda POINT + clc + adc #YRES + sta POINT + bcc @L1 + inc POINT+1 +@L1: rts +.endproc diff --git a/libsrc/vic20/tgi_stat_stddrv.s b/libsrc/vic20/tgi_stat_stddrv.s new file mode 100644 index 000000000..6a94d66aa --- /dev/null +++ b/libsrc/vic20/tgi_stat_stddrv.s @@ -0,0 +1,8 @@ +; +; Address of the static standard TGI driver +; +; const void tgi_static_stddrv[]; +; + + .import _vic20_hi_tgi + .export _tgi_static_stddrv := _vic20_hi_tgi diff --git a/libsrc/vic20/tgi_stddrv.s b/libsrc/vic20/tgi_stddrv.s new file mode 100644 index 000000000..d56af37e7 --- /dev/null +++ b/libsrc/vic20/tgi_stddrv.s @@ -0,0 +1,13 @@ +; +; Name of the standard TGI driver +; +; 2018-03-11, Sven Michael Klose +; +; const char tgi_stddrv[]; +; + + .export _tgi_stddrv + +.rodata + +_tgi_stddrv: .asciiz "vic20-hi.tgi" diff --git a/libsrc/vic20/tgihdr.s b/libsrc/vic20/tgihdr.s new file mode 100644 index 000000000..30258162d --- /dev/null +++ b/libsrc/vic20/tgihdr.s @@ -0,0 +1,67 @@ +; +; This code sits immediately after the BASIC stub program. +; Therefore, it's executed by that stub. +; +; 2018-04-17, Greg King +; + + .export __TGIHDR__ : abs = 1 ; Mark as TGI housekeeper + + .import __MAIN_LAST__ + .importzp ptr1, ptr2, tmp1 + + +basic_reset := $C000 ; vector to BASIC's cold-start code + +; This code moves the program to $2000. That move allows $1000 - $1FFF +; to be used by the TGI driver to hold its graphics data. + +.segment "TGI1HDR" + + lda #<(tgi1end + prog_size) ; source + ldx #>(tgi1end + prog_size) + sta ptr1 + stx ptr1+1 + + lda #<(tgi2hdr + prog_size) ; destination + ldx #>(tgi2hdr + prog_size) + sta ptr2 + stx ptr2+1 + + ldx #<~prog_size + lda #>~prog_size ; use -(prog_size + 1) + sta tmp1 + + ldy #<$0000 + +; Copy loop + +@L1: inx ; bump counter's low byte + beq @L4 + +@L2: tya ; will .Y underflow? + bne @L3 + dec ptr1+1 ; yes, do next lower page + dec ptr2+1 +@L3: dey + + lda (ptr1),y + sta (ptr2),y + jmp @L1 + +@L4: inc tmp1 ; bump counter's high byte + bne @L2 + + jmp tgi2hdr ; go to moved program +tgi1end: + +.segment "TGI2HDR" + +tgi2hdr: + jsr tgi2end ; run actual program + jmp (basic_reset) +tgi2end: + +; The length of the TGI program (including the TGI2HDR segment) + +prog_size = __MAIN_LAST__ - tgi2hdr diff --git a/samples/Makefile b/samples/Makefile index adfe870e4..267dc5235 100644 --- a/samples/Makefile +++ b/samples/Makefile @@ -230,6 +230,20 @@ ovrldemo: overlaydemo.o OVERLAYLIST := $(foreach I,1 2 3,multdemo.$I ovrldemo.$I) +# -------------------------------------------------------------------------- +# TGI programs on the VIC-20 need a special ld65 configuration file. + +ifeq ($(SYS),vic20) +mandelbrot.o: override CFLAGS += -D DYN_DRV=0 +mandelbrot: mandelbrot.o + $(LD) $(LDFLAGS) -o $@ -C vic20-tgi.cfg -m $@.map $^ $(SYS).lib + +# tgidemo needs at least 16K of RAM expansion. +tgidemo.o: override CFLAGS += -D DYN_DRV=0 +tgidemo: tgidemo.o + $(LD) -D __HIMEM__=0x6000 $(LDFLAGS) -o $@ -C vic20-tgi.cfg -m $@.map $^ $(SYS).lib +endif + # -------------------------------------------------------------------------- # Rule to make a CBM disk with all samples. Needs the c1541 program that comes # with the VICE emulator. diff --git a/samples/tgidemo.c b/samples/tgidemo.c index 93e91899b..b431cfb98 100644 --- a/samples/tgidemo.c +++ b/samples/tgidemo.c @@ -70,9 +70,10 @@ static void DoCircles (void) { static const unsigned char Palette[2] = { TGI_COLOR_WHITE, TGI_COLOR_ORANGE }; unsigned char I; - unsigned char Color = COLOR_FORE; - unsigned X = MaxX / 2; - unsigned Y = MaxY / 2; + unsigned char Color = COLOR_BACK; + const unsigned X = MaxX / 2; + const unsigned Y = MaxY / 2; + const unsigned Limit = (X < Y) ? Y : X; tgi_setpalette (Palette); tgi_setcolor (COLOR_FORE); @@ -82,7 +83,7 @@ static void DoCircles (void) while (!kbhit ()) { Color = (Color == COLOR_FORE) ? COLOR_BACK : COLOR_FORE; tgi_setcolor (Color); - for (I = 10; I < 240; I += 10) { + for (I = 10; I <= Limit; I += 10) { tgi_ellipse (X, Y, I, tgi_imulround (I, AspectRatio)); } } @@ -132,7 +133,7 @@ static void DoDiagram (void) tgi_setcolor (COLOR_FORE); tgi_clear (); - /* Determine zero and aplitude */ + /* Determine zero and amplitude */ YOrigin = MaxY / 2; XOrigin = 10; Amp = (MaxY - 19) / 2; @@ -168,16 +169,17 @@ static void DoLines (void) { static const unsigned char Palette[2] = { TGI_COLOR_WHITE, TGI_COLOR_BLACK }; unsigned X; + const unsigned Min = (MaxX < MaxY) ? MaxX : MaxY; tgi_setpalette (Palette); tgi_setcolor (COLOR_FORE); tgi_clear (); - for (X = 0; X <= MaxY; X += 10) { - tgi_line (0, 0, MaxY, X); - tgi_line (0, 0, X, MaxY); - tgi_line (MaxY, MaxY, 0, MaxY-X); - tgi_line (MaxY, MaxY, MaxY-X, 0); + for (X = 0; X <= Min; X += 10) { + tgi_line (0, 0, Min, X); + tgi_line (0, 0, X, Min); + tgi_line (Min, Min, 0, Min-X); + tgi_line (Min, Min, Min-X, 0); } cgetc ();