diff --git a/cfg/plus4-hires.cfg b/cfg/plus4-hires.cfg new file mode 100644 index 000000000..0426c8d8c --- /dev/null +++ b/cfg/plus4-hires.cfg @@ -0,0 +1,56 @@ +# Linker configuration that allows for a hi-res bitmap at $C000-$DF3F, but +# puts the stack (and a "HIBSS" segment) in the remaining RAM at $DF40-$FD00. + +FEATURES { + STARTADDRESS: default = $1001; +} +SYMBOLS { + __LOADADDR__: type = import; + __EXEHDR__: type = import; + __STACKSIZE__: type = weak, value = $0800; # 2k stack + __HIMEM__: type = weak, value = $FD00; +} +MEMORY { + # Reserve 8000 bytes at $C000 for 320x200 bitmap + RESERVED: file = "", define = yes, start = $C000, size = 8000; + + ZP: file = "", define = yes, start = $0002, size = $001A; + LOADADDR: file = %O, start = %S - 2, size = $0002; + HEADER: file = %O, define = yes, start = %S, size = $000D; + MAIN: file = %O, define = yes, start = __HEADER_LAST__, size = __RESERVED_START__ - __MAIN_START__; + + # Space between bitmap and top of memory + HIRAM: file = "", define = yes, start = __RESERVED_LAST__, size = __HIMEM__ - __HIRAM_START__ - __STACKSIZE__; +} +SEGMENTS { + ZEROPAGE: load = ZP, type = zp; + LOADADDR: load = LOADADDR, type = ro; + EXEHDR: load = HEADER, type = ro; + STARTUP: load = MAIN, type = ro; + LOWCODE: load = MAIN, type = ro, optional = yes; + CODE: load = MAIN, type = ro; + ONCE: load = MAIN, type = ro, optional = yes; + RODATA: load = MAIN, type = ro; + DATA: load = MAIN, type = rw; + INIT: load = MAIN, type = bss; + BSS: load = MAIN, type = bss, define = yes; + + # Allow data between bitmap and top of memory to be used as a second BSS + # space. Define symbols for it so that it can be supplied to _heapadd(). + HIBSS: load = HIRAM, type = bss, optional = yes, 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/plus4.sgml b/doc/plus4.sgml index 79a2597d0..578f61c33 100644 --- a/doc/plus4.sgml +++ b/doc/plus4.sgml @@ -165,8 +165,26 @@ 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 Plus/4. + + + This driver features a resolution of 320*200 with two colors and an + adjustable palette (that means that the two colors can be chosen out of a + palette of the 121 TED colors). + Note that the text-mode character matrix and color data are destroyed by this + driver. The driver calls the Kernal

Extended memory drivers

diff --git a/include/cbm264.h b/include/cbm264.h index ab634b721..c220bf407 100644 --- a/include/cbm264.h +++ b/include/cbm264.h @@ -110,7 +110,23 @@ #define COLOR_LIGHTBLUE (BCOLOR_LIGHTBLUE | CATTR_LUMA7) #define COLOR_GRAY3 (BCOLOR_WHITE | CATTR_LUMA5) - +/* TGI color defines */ +#define TGI_COLOR_BLACK (BCOLOR_BLACK) +#define TGI_COLOR_WHITE (BCOLOR_WHITE | CATTR_LUMA7) +#define TGI_COLOR_RED (BCOLOR_RED | CATTR_LUMA4) +#define TGI_COLOR_CYAN (BCOLOR_CYAN | CATTR_LUMA7) +#define TGI_COLOR_PURPLE (BCOLOR_LIGHTVIOLET | CATTR_LUMA7) +#define TGI_COLOR_GREEN (BCOLOR_GREEN | CATTR_LUMA7) +#define TGI_COLOR_BLUE (BCOLOR_BLUE | CATTR_LUMA7) +#define TGI_COLOR_YELLOW (BCOLOR_YELLOW | CATTR_LUMA7) +#define TGI_COLOR_ORANGE (BCOLOR_ORANGE | CATTR_LUMA7) +#define TGI_COLOR_BROWN (BCOLOR_BROWN | CATTR_LUMA7) +#define TGI_COLOR_LIGHTRED (BCOLOR_RED | CATTR_LUMA7) +#define TGI_COLOR_GRAY1 (BCOLOR_WHITE | CATTR_LUMA1) +#define TGI_COLOR_GRAY2 (BCOLOR_WHITE | CATTR_LUMA3) +#define TGI_COLOR_LIGHTGREEN (BCOLOR_LIGHTGREEN | CATTR_LUMA7) +#define TGI_COLOR_LIGHTBLUE (BCOLOR_LIGHTBLUE | CATTR_LUMA7) +#define TGI_COLOR_GRAY3 (BCOLOR_WHITE | CATTR_LUMA5) /* Masks for joy_read */ #define JOY_UP_MASK 0x01 diff --git a/include/plus4.h b/include/plus4.h index 7730938e8..5eb14d24d 100644 --- a/include/plus4.h +++ b/include/plus4.h @@ -57,8 +57,7 @@ /* The addresses of the static drivers */ extern void plus4_stdjoy_joy[]; /* Referred to by joy_static_stddrv[] */ extern void plus4_stdser_ser[]; /* Referred to by ser_static_stddrv[] */ - - +extern void ted_hi_tgi[]; /* End of plus4.h */ #endif diff --git a/libsrc/plus4/cgetc.s b/libsrc/plus4/cgetc.s index 62863c06e..8c8bb0082 100644 --- a/libsrc/plus4/cgetc.s +++ b/libsrc/plus4/cgetc.s @@ -18,11 +18,16 @@ _cgetc: lda KEY_COUNT ; Get number of characters ora FKEY_COUNT ; Or with number of function key chars bne L2 ; Jump if there are already chars waiting + lda #%00100000 + bit $FF06 + bne L2 ; always disable cursor if in bitmap mode + ; Switch on the cursor if needed ldy CURS_X lda (CRAM_PTR),y ; Get current char pha ; And save it + lda CHARCOLOR sta (CRAM_PTR),y diff --git a/libsrc/plus4/crt0.s b/libsrc/plus4/crt0.s index 6b44a2b7e..d1eea9472 100644 --- a/libsrc/plus4/crt0.s +++ b/libsrc/plus4/crt0.s @@ -9,8 +9,7 @@ .import callirq_y, initlib, donelib .import callmain, zerobss .import __INTERRUPTOR_COUNT__ - .import __MAIN_START__, __MAIN_SIZE__ ; Linker generated - .import __STACKSIZE__ ; Linker generated + .import __HIMEM__ ; Linker generated .importzp ST .include "zeropage.inc" @@ -52,19 +51,28 @@ L1: lda sp,x tsx stx spsave ; Save system stk ptr - lda #<(__MAIN_START__ + __MAIN_SIZE__ + __STACKSIZE__) - ldx #>(__MAIN_START__ + __MAIN_SIZE__ + __STACKSIZE__) + lda #<__HIMEM__ + ldx #>__HIMEM__ sta sp stx sp+1 ; Set up the IRQ vector in the banked RAM; and, switch off the ROM. - ldx #IRQ + lda #IRQ sei ; No ints, handler not yet in place sta ENABLE_RAM - stx $FFFE ; Install interrupt handler - sty $FFFF + sta $FFFE ; Install interrupt handler + stx $FFFF + lda IRQVec + ldx IRQVec+1 + sta IRQInd+1 + stx IRQInd+2 + lda #IRQStub + sta IRQVec + stx IRQVec+1 + cli ; Allow interrupts ; Clear the BSS data. @@ -95,6 +103,13 @@ _exit: pha ; Save the return code lda #0 sta irqcount ; Disable custom IRQ handlers + sei + ldx IRQInd+1 + ldy IRQInd+2 + stx IRQVec + sty IRQVec+1 + cli + ; Copy back the zero-page stuff. ldx #zpspace-1 @@ -122,9 +137,13 @@ L2: lda zpsave,x ; IRQ handler. The handler in the ROM enables the Kernal, and jumps to ; $CE00, where the ROM code checks for a BRK or IRQ, and branches via the ; indirect vectors at $314/$316. -; To make our stub as fast as possible, we skip the whole part of the ROM -; handler, and jump to the indirect vectors directly. We do also call our -; own interrupt handlers if we have any; so, they need not use $314. +; +; When RAM is banked in, we skip the whole part of the ROM handler, and jump to +; the indirect vectors directly, after calling our own interrupt handlers. +; +; When ROM is banked in, a stub installed in the $314 indirect vector ensures +; that our interrupt handlers are still called (otherwise, interrupts that are +; not serviced by the ROM handler may cause a deadlock). .segment "LOWCODE" @@ -139,15 +158,6 @@ IRQ: cld ; Just to be sure and #$10 ; Test for BRK bit bne dobreak -; It's an IRQ; and, RAM is enabled. If we have handlers, call them. We will use -; a flag here instead of loading __INTERRUPTOR_COUNT__ directly, since the -; condes function is not reentrant. The irqcount flag will be set/reset from -; the main code, to avoid races. - - ldy irqcount - beq @L1 - jsr callirq_y ; Call the IRQ functions - ; Since the ROM handler will end with an RTI, we have to fake an IRQ return ; on the stack, so that we get control of the CPU after the ROM handler, ; and can switch back to RAM. @@ -161,7 +171,7 @@ IRQ: cld ; Just to be sure pha ; Push faked X register pha ; Push faked Y register sta ENABLE_ROM ; Switch to ROM - jmp (IRQVec) ; Jump indirect to Kernal IRQ handler + jmp (IRQVec) ; Jump indirect to IRQ stub irq_ret: sta ENABLE_RAM ; Switch back to RAM @@ -183,6 +193,22 @@ nohandler: sta ENABLE_ROM jmp (BRKVec) ; Jump indirect to the break vector + +; IRQ stub installed at $314, called by our handler above if RAM is banked in, +; or the Kernal IRQ handler if ROM is banked in. + +; If we have handlers, call them. We will use a flag here instead of loading +; __INTERRUPTOR_COUNT__ directly, since the condes function is not reentrant. +; The irqcount flag will be set/reset from the main code, to avoid races. +IRQStub: + cld ; Just to be sure + sta ENABLE_RAM + ldy irqcount + beq @L1 + jsr callirq_y ; Call the IRQ functions +@L1: sta ENABLE_ROM + jmp (IRQInd+1) ; Jump to the saved IRQ vector + ; ------------------------------------------------------------------------ ; Data diff --git a/libsrc/plus4/libref.s b/libsrc/plus4/libref.s index 0bda1e7e8..62c78b8c5 100644 --- a/libsrc/plus4/libref.s +++ b/libsrc/plus4/libref.s @@ -2,8 +2,9 @@ ; Oliver Schmidt, 2013-05-31 ; - .export joy_libref, ser_libref + .export joy_libref, ser_libref, tgi_libref .import _exit joy_libref := _exit ser_libref := _exit +tgi_libref := _exit diff --git a/libsrc/plus4/ser/plus4-stdser.s b/libsrc/plus4/ser/plus4-stdser.s index 77445c7a2..38809d6ab 100644 --- a/libsrc/plus4/ser/plus4-stdser.s +++ b/libsrc/plus4/ser/plus4-stdser.s @@ -64,15 +64,15 @@ ACIA_STATUS := ACIA+1 ; Status register ACIA_CMD := ACIA+2 ; Command register ACIA_CTRL := ACIA+3 ; Control register +RecvHead := $07D1 ; Head of receive buffer +RecvTail := $07D2 ; Tail of receive buffer +RecvFreeCnt := $07D3 ; Number of bytes in receive buffer ;---------------------------------------------------------------------------- ; ; Global variables ; .bss -RecvHead: .res 1 ; Head of receive buffer -RecvTail: .res 1 ; Tail of receive buffer -RecvFreeCnt: .res 1 ; Number of bytes in receive buffer SendHead: .res 1 ; Head of send buffer SendTail: .res 1 ; Tail of send buffer SendFreeCnt: .res 1 ; Number of bytes in send buffer @@ -88,7 +88,7 @@ SendBuf: .res 256 ; Tables used to translate RS232 params into register values -BaudTable: ; bit7 = 1 means setting is invalid +BaudTable: ; Bit7 = 1 means setting is invalid .byte $FF ; SER_BAUD_45_5 .byte $01 ; SER_BAUD_50 .byte $02 ; SER_BAUD_75 @@ -354,26 +354,27 @@ SER_IOCTL: ; SER_IRQ: - lda ACIA_STATUS ; Check ACIA status for receive interrupt - and #$08 - beq @L9 ; Jump if no ACIA interrupt (carry still clear) - lda ACIA_DATA ; Get byte from ACIA - ldx RecvFreeCnt ; Check if we have free space left - beq @L1 ; Jump if no space in receive buffer - ldy RecvTail ; Load buffer pointer - sta RecvBuf,y ; Store received byte in buffer - inc RecvTail ; Increment buffer pointer - dec RecvFreeCnt ; Decrement free space counter - cpx #33 ; Check for buffer space low - bcc @L1 ; Assert flow control if buffer space low + lda ACIA_STATUS ; (4) Check for byte received + and #$08 ; (2) + beq @L9 ; (2*) + + lda ACIA_DATA ; (4) Get byte and put into receive buffer + ldy RecvTail ; (4) + ldx RecvFreeCnt ; (4) + beq @L3 ; (2*) Jump if no space in receive buffer + sta RecvBuf,y ; (5) + inc RecvTail ; (6) + dec RecvFreeCnt ; (6) + cpx #33 ; (2) Check for buffer space low + bcc @L2 ; (2*) rts ; Return with carry set (interrupt handled) ; Assert flow control if buffer space too low -@L1: lda RtsOff - sta ACIA_CMD - sta Stopped - sec ; Interrupt handled +@L2: lda RtsOff ; (3) + sta ACIA_CMD ; (4) + sta Stopped ; (3) +@L3: sec ; Interrupt handled @L9: rts ;---------------------------------------------------------------------------- diff --git a/libsrc/plus4/tgi/ted-hi.s b/libsrc/plus4/tgi/ted-hi.s new file mode 100644 index 000000000..61dd9c2c4 --- /dev/null +++ b/libsrc/plus4/tgi/ted-hi.s @@ -0,0 +1,860 @@ +; +; Graphics driver for the 320x200x2 mode on Commodore Plus/4 systems. +; +; Luminance/Chrominance matrices at $800/$C00, overwriting text-mode character +; and color data. Bitmap is at $C000-$DF3F. Programs using this driver should +; either be linked with the option '-D __HIMEM__=0xC000'. +; +; Based on the c64-hi TGI driver, which in turn was based on Stephen L. Judd's +; GRLIB code. +; +; 2017-01-13, Greg King +; 2018-03-13, Sven Klose +; 2019-10-23, Richard Halkyard +; + .include "zeropage.inc" + + .include "tgi-kernel.inc" + .include "tgi-error.inc" + + .include "cbm_kernal.inc" + .include "plus4.inc" + + .macpack generic + .macpack module + +; ------------------------------------------------------------------------ +; Header. Includes jump table and constants. + + module_header _ted_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 ; "tgi" + .byte TGI_API_VERSION ; TGI API version number + .addr $0000 ; Library reference + .word 320 ; X resolution + .word 200 ; 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 $00D4 ; Aspect ratio (based on 4/3 display) + .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 + +TEMP := tmp4 +TEMP2 := sreg +POINT := regsave + +CHUNK := X2 ; Used in the line routine +OLDCHUNK := X2+1 ; Dito + +; Absolute variables used in the code + +.bss + +ERROR: .res 1 ; Error code +PALETTE: .res 2 ; The current palette + +BITMASK: .res 1 ; $00 = clear, $FF = set pixels + +; Line routine stuff +DX: .res 2 +DY: .res 2 + +; BAR variables +X1SAVE: .res 2 +Y1SAVE: .res 2 +X2SAVE: .res 2 +Y2SAVE: .res 2 + +; Text output stuff +TEXTMAGX: .res 1 +TEXTMAGY: .res 1 +TEXTDIR: .res 1 + +; Constants and tables + +.rodata + +DEFPALETTE: .byte $00, $71 ; 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 := $D000 ; Character rom base address + +; The TED uses the CPU's memory configuration to fetch color data! Although +; we run with ROMs banked out, putting color data in banked RAM (above $8000) +; will result in color artifacts appearing when we bank ROM back in for +; interrupts and Kernal calls. Bitmap data is not affected by this limitation, +; but since there is no way to access RAM under IO (FE00-FF40), we can't put the +; bitmap at $E000 like we do on the C64, and have to use the next lowest +; position at $C000. + +LBASE := $0800 ; Luminance memory base address +VBASE := $C000 ; Bitmap base address + +CBASE := LBASE + $400 ; Chrominance memory base address (fixed relative to LBASE) +CHRBASE := $0800 ; Base address of text mode data + +.assert LBASE .mod $0800 = 0, error, "Luma/Chroma memory base address must be a multiple of 2K" +.assert VBASE .mod $2000 = 0, error, "Bitmap base address must be a multiple of 8K" +.assert LBASE + $800 < $8000, warning, "Luma/Chroma memory overlaps ROM. This will produce color artifacts." +.assert VBASE + $2000 < $FE00, error, "Bitmap overlaps IO space" + +.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: +; rts ; 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 only called once, so any code that is needed +; to initializes variables and so on must go here. Setting palette and +; clearing the screen is not needed because this is called by the graphics +; kernel later. +; The graphics kernel will never call INIT when a graphics mode is already +; active, so there is no need to protect against that. +; +; Must set an error code: YES +; + +INIT: + +; Initialize variables + + ldx #$FF + stx BITMASK + +; Switch into graphics mode + lda $FF12 ; Set bitmap address and enable fetch from RAM + and #%00000011 + ora #(>VBASE >> 2) + sta $FF12 + +.if LBASE <> CHRBASE + lda #>LBASE ; Set color memory address + sta $FF14 +.endif + + lda $FF06 ; Enable bitmap mode + ora #%00100000 + sta $FF06 + +; Done, reset the error code + + lda #TGI_ERR_OK + sta ERROR + rts + +; ------------------------------------------------------------------------ +; DONE: Will be called to switch the graphics device back into text mode. +; The graphics kernel will never call DONE when no graphics mode is active, +; so there is no need to protect against that. +; +; Must set an error code: NO +; + +DONE: lda $FF12 + ora #%00000100 ; Fetch from ROM + sta $FF12 + +.if LBASE <> CHRBASE + lda #>CHRBASE ; Reset character/color matrix address + sta $FF14 +.else + sta ENABLE_ROM ; Clear text display since we clobbered it + jsr CLRSCR + sta ENABLE_RAM +.endif + + lda $FF06 + and #%11011111 ; Exit bitmap mode + sta $FF06 + + rts + +; ------------------------------------------------------------------------ +; GETERROR: Return the error code in A and clear it. + +GETERROR: + ldx #TGI_ERR_OK + lda ERROR + stx ERROR + rts + +; ------------------------------------------------------------------------ +; CONTROL: Platform/driver specific entry point. +; +; Must set an error code: YES +; + +CONTROL: + lda #TGI_ERR_INV_FUNC + sta ERROR + rts + +; ------------------------------------------------------------------------ +; CLEAR: Clears the screen. +; +; Must set an error code: NO +; + +CLEAR: ldy #$00 + tya +@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 + sta VBASE+$0F00,y + sta VBASE+$1000,y + sta VBASE+$1100,y + sta VBASE+$1200,y + sta VBASE+$1300,y + sta VBASE+$1400,y + sta VBASE+$1500,y + sta VBASE+$1600,y + sta VBASE+$1700,y + sta VBASE+$1800,y + sta VBASE+$1900,y + sta VBASE+$1A00,y + sta VBASE+$1B00,y + sta VBASE+$1C00,y + sta VBASE+$1D00,y + sta VBASE+$1E00,y + sta VBASE+$1E40,y + iny + bne @L1 + rts + +; ------------------------------------------------------------------------ +; SETVIEWPAGE: Set the visible page. Called with the new page in A (0..n). +; The page number is already checked to be valid by the graphics kernel. +; +; Must set an error code: NO (will only be called if page ok) +; + +SETVIEWPAGE: +; rts ; Fall through + +; ------------------------------------------------------------------------ +; SETDRAWPAGE: Set the drawable page. Called with the new page in A (0..n). +; The page number is already checked to be valid by the graphics kernel. +; +; Must set an error code: NO (will only be called if page ok) +; + +SETDRAWPAGE: + rts + +; ------------------------------------------------------------------------ +; SETCOLOR: Set the drawing color (in A). The new color is already checked +; to be in a valid range (0..maxcolor-1). +; +; Must set an error code: NO (will only be called if color ok) +; + +SETCOLOR: + tax + beq @L1 + lda #$FF +@L1: sta BITMASK + 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: + ldy #PALETTESIZE - 1 +@L1: lda (ptr1),y ; Copy the palette + sta PALETTE,y + dey + bpl @L1 + +; Get luma values from the high nybble of the palette entries + lda PALETTE+1 ; Foreground luma + lsr a + lsr a + lsr a + lsr a + sta TEMP ; Foreground -> low nybble + lda PALETTE ; Background luma + and #$F0 + ora TEMP ; Background -> high nybble + +; Initialize the luma map with the new luma values + ldy #0 +@L2: sta LBASE+$0000,y + sta LBASE+$0100,y + sta LBASE+$0200,y + sta LBASE+$02e8,y + iny + bne @L2 + + +; Get chroma values from the low nybble of the palette entries + lda PALETTE+1 ; Foreground chroma + and #$0F + asl a + asl a + asl a + asl a + sta TEMP ; Foreground -> high nybble + lda PALETTE ; Background chroma + and #$0F + ora TEMP ; Background -> low nybble + +; Initialize the chroma map with the new chroma values + ldy #0 +@L3: sta CBASE+$0000,y + sta CBASE+$0100,y + sta CBASE+$0200,y + sta CBASE+$02e8,y + iny + bne @L3 + +; Done, reset the error code + lda #TGI_ERR_OK + sta ERROR + rts + +; ------------------------------------------------------------------------ +; GETPALETTE: Return the current palette in A/X. 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 A/X. 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) +; + +GETDEFPALETTE: + lda #DEFPALETTE + rts + +; ------------------------------------------------------------------------ +; SETPIXEL: Draw one pixel at X1/Y1 = ptr1/ptr2 with the current drawing +; color. The coordinates passed to this function are never outside the +; visible screen area, so there is no need for clipping inside this function. +; +; Must set an error code: NO +; + +SETPIXEL: + jsr CALC ; Calculate coordinates + + lda (POINT),Y + eor BITMASK + and BITTAB,X + eor (POINT),Y + sta (POINT),Y + +@L9: rts + +; ------------------------------------------------------------------------ +; GETPIXEL: Read the color value of a pixel and return it in A/X. The +; coordinates passed to this function are never outside the visible screen +; area, so there is no need for clipping inside this function. + + +GETPIXEL: + jsr CALC ; Calculate coordinates + + lda (POINT),Y + ldy #$00 + and BITTAB,X + beq @L1 + iny + +@L1: + tya ; Get color value into A + ldx #$00 ; Clear high byte + 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. +; +; X1,X2 etc. are set up above (x2=LINNUM in particular) +; Format is LINE x2,y2,x1,y1 +; +; Must set an error code: NO +; + +LINE: + +@CHECK: lda X2 ; Make sure x1=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 + + jsr CALC ; Set up .X, .Y, and POINT + 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 +; +; To simplify my life, just use PLOT to plot points. +; +; No more! +; Added special plotting routine -- cool! +; +; X is now counter, Y is y-coordinate +; +; On entry, X=DY=number of loop iterations, and Y= +; Y1 AND #$07 +STEPINY: + lda #00 + sta OLDCHUNK ; So plotting routine will work right + lda CHUNK + lsr ; Strip the bit + eor CHUNK + sta CHUNK + txa + beq YCONT2 ; If dy=0, it's just a point +@CONT: lsr ; Init counter to dy/2 +; +; Main loop +; +YLOOP: sta TEMP + + lda (POINT),y + eor BITMASK + and CHUNK + eor (POINT),y + sta (POINT),y +YINCDEC: + iny ; Advance Y coordinate + cpy #8 + bcc @CONT ; No prob if Y=0..7 + jsr FIXY +@CONT: 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 ; And add 8 to POINT + adc #8 + 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 AND #$07 + +.bss +COUNTHI: + .byte $00 ; Temporary counter, only used 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 #8 + 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-coordinate. +; +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-coord + cpy #8 ; 0..7 is ok + bcc XCONT2 + sta TEMP + jsr FIXY + lda TEMP + 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 + +; +; Subroutine to fix up pointer when Y decreases through +; zero or increases through 7. +; +FIXY: cpy #255 ; Y=255 or Y=8 + beq @DECPTR + +@INCPTR: ; Add 320 to pointer + ldy #0 ; Y increased through 7 + lda POINT + adc #<320 + sta POINT + lda POINT+1 + adc #>320 + sta POINT+1 + rts + +@DECPTR: ; Okay, subtract 320 then + ldy #7 ; Y decreased through 0 + lda POINT + sec + sbc #<320 + sta POINT + lda POINT+1 + sbc #>320 + sta POINT+1 + rts + +; ------------------------------------------------------------------------ +; 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 coordinates 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 +; + +; Note: This function needs optimization. It's just a cheap translation of +; the original C wrapper and could be written much smaller (besides that, +; calling LINE is not a good idea either). + +BAR: lda Y2 + sta Y2SAVE + lda Y2+1 + sta Y2SAVE+1 + + lda X2 + sta X2SAVE + lda X2+1 + sta X2SAVE+1 + + lda Y1 + sta Y1SAVE + lda Y1+1 + sta Y1SAVE+1 + + lda X1 + sta X1SAVE + lda X1+1 + sta X1SAVE+1 + +@L1: lda Y1 + sta Y2 + lda Y1+1 + sta Y2+1 + jsr LINE + + lda Y1SAVE + cmp Y2SAVE + bne @L2 + lda Y1SAVE + cmp Y2SAVE + beq @L4 + +@L2: inc Y1SAVE + bne @L3 + inc Y1SAVE+1 + +@L3: lda Y1SAVE + sta Y1 + lda Y1SAVE+1 + sta Y1+1 + + lda X1SAVE + sta X1 + lda X1SAVE+1 + sta X1+1 + + lda X2SAVE + sta X2 + lda X2SAVE+1 + sta X2+1 + jmp @L1 + +@L4: rts + + +; ------------------------------------------------------------------------ +; TEXTSTYLE: Set the style used when calling OUTTEXT. Text scaling in X and Y +; direction is passend in X/Y, the text direction is passed in A. +; +; Must set an error code: NO +; + +TEXTSTYLE: + stx TEXTMAGX + sty TEXTMAGY + sta TEXTDIR + 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: + rts + +; ------------------------------------------------------------------------ +; Calculate all variables to plot the pixel at X1/Y1. + +CALC: lda Y1 + sta TEMP2 + and #7 + tay + lda Y1+1 + lsr ; Neg is possible + ror TEMP2 + lsr + ror TEMP2 + lsr + ror TEMP2 + + lda #00 + sta POINT + lda TEMP2 + cmp #$80 + ror + ror POINT + cmp #$80 + ror + ror POINT ; Row * 64 + adc TEMP2 ; + Row * 256 + clc + adc #>VBASE ; + Bitmap base + sta POINT+1 + + lda X1 + tax + and #$F8 + clc + adc POINT ; +(X AND #$F8) + sta POINT + lda X1+1 + adc POINT+1 + sta POINT+1 + txa + and #7 + tax + rts diff --git a/libsrc/plus4/tgi_stat_stddrv.s b/libsrc/plus4/tgi_stat_stddrv.s new file mode 100644 index 000000000..dc918eb8b --- /dev/null +++ b/libsrc/plus4/tgi_stat_stddrv.s @@ -0,0 +1,14 @@ +; +; Address of the static standard tgi driver +; +; Oliver Schmidt, 2012-11-01 +; +; const void tgi_static_stddrv[]; +; + + .export _tgi_static_stddrv + .import _ted_hi_tgi + +.rodata + +_tgi_static_stddrv := _ted_hi_tgi diff --git a/libsrc/plus4/tgi_stddrv.s b/libsrc/plus4/tgi_stddrv.s new file mode 100644 index 000000000..eac16905d --- /dev/null +++ b/libsrc/plus4/tgi_stddrv.s @@ -0,0 +1,13 @@ +; +; Name of the standard tgi driver +; +; Oliver Schmidt, 2011-05-02 +; +; const char tgi_stddrv[]; +; + + .export _tgi_stddrv + +.rodata + +_tgi_stddrv: .asciiz "ted-hi.tgi" diff --git a/samples/Makefile b/samples/Makefile index 295b6883d..d9bda45d4 100644 --- a/samples/Makefile +++ b/samples/Makefile @@ -99,6 +99,7 @@ DISK_apple2 = samples.dsk DISK_apple2enh = samples.dsk DISK_atari = samples.atr DISK_atarixl = samples.atr +DISK_plus4 = samples.d64 # -------------------------------------------------------------------------- # System-dependent settings @@ -146,6 +147,8 @@ LDFLAGS_tgidemo_atarixl = --start-addr 0x4000 .o: ifeq ($(SYS),vic20) $(LD) $(LDFLAGS_$(@F)_$(SYS)) $(LDFLAGS) -o $@ -C vic20-32k.cfg -m $@.map $^ $(SYS).lib +else ifeq ($(SYS),plus4) + $(LD) $(LDFLAGS_$(@F)_$(SYS)) $(LDFLAGS) -o $@ -C plus4-hires.cfg -m $@.map $^ $(SYS).lib else $(LD) $(LDFLAGS_$(@F)_$(SYS)) $(LDFLAGS) -o $@ -t $(SYS) -m $@.map $^ $(SYS).lib endif @@ -326,9 +329,11 @@ EXELIST_plus4 = \ enumdevdir \ gunzip65 \ hello \ + mandelbrot \ terminal \ tinyshell \ - sieve + sieve \ + tgidemo EXELIST_sim6502 = \ checkversion \ @@ -451,7 +456,15 @@ multdemo: multidemo.o ovrldemo: overlaydemo.o $(LD) $(LDFLAGS) -o $@ -C $(SYS)-overlay.cfg -m $@.map $^ $(SYS).lib -OVERLAYLIST := $(foreach I,1 2 3,multdemo.$I ovrldemo.$I) +OVERLAYLIST := + +ifneq ($(filter ovrldemo,$(EXELIST_$(SYS))),) +OVERLAYLIST += $(foreach I,1 2 3,ovrldemo.$I) +endif + +ifneq ($(filter multdemo,$(EXELIST_$(SYS))),) +OVERLAYLIST += $(foreach I,1 2 3,multdemo.$I) +endif # -------------------------------------------------------------------------- # TGI programs on the VIC-20 need a special ld65 configuration file. diff --git a/samples/tgidemo.c b/samples/tgidemo.c index f193a1b83..cdb4c3ad9 100644 --- a/samples/tgidemo.c +++ b/samples/tgidemo.c @@ -12,8 +12,10 @@ # define DYN_DRV 1 #endif -#define COLOR_BACK TGI_COLOR_BLACK -#define COLOR_FORE TGI_COLOR_WHITE + +/* Color values passed to TGI functions are indices into the default palette. */ +#define COLOR_BACK 0 +#define COLOR_FORE 1 /*****************************************************************************/ @@ -65,17 +67,39 @@ static void DoWarning (void) #endif +/* + * Note that everywhere else in the TGI API, colors are referred to via an index + * to the current palette. + * + * TGI_COLOR_ values can be used (ONLY!) for setting the palette, using them + * with other TGI functions only works by chance, on some targets. + */ +static void DoPalette (int n) +{ + static const unsigned char Palette[4][2] = { +/* FIXME: add some ifdefs with proper values for targets that need it */ +#if !defined(__APPLE2__) + { TGI_COLOR_BLACK, TGI_COLOR_BLUE }, + { TGI_COLOR_WHITE, TGI_COLOR_BLACK }, + { TGI_COLOR_RED, TGI_COLOR_BLACK }, +#else + { TGI_COLOR_WHITE, TGI_COLOR_BLACK }, + { TGI_COLOR_BLACK, TGI_COLOR_WHITE }, + { TGI_COLOR_WHITE, TGI_COLOR_BLACK }, +#endif + }; + tgi_setpalette (Palette[n]); +} + static void DoCircles (void) { - static const unsigned char Palette[2] = { TGI_COLOR_WHITE, TGI_COLOR_BLUE }; unsigned char I; 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); tgi_clear (); tgi_line (0, 0, MaxX, MaxY); @@ -87,7 +111,9 @@ static void DoCircles (void) tgi_ellipse (X, Y, I, tgi_imulround (I, AspectRatio)); } } - + while (kbhit ()) { + cgetc (); + } cgetc (); } @@ -95,11 +121,9 @@ static void DoCircles (void) static void DoCheckerboard (void) { - static const unsigned char Palette[2] = { TGI_COLOR_WHITE, TGI_COLOR_BLACK }; unsigned X, Y; unsigned char Color = COLOR_BACK; - tgi_setpalette (Palette); tgi_clear (); while (1) { @@ -123,13 +147,11 @@ static void DoCheckerboard (void) static void DoDiagram (void) { - static const unsigned char Palette[2] = { TGI_COLOR_WHITE, TGI_COLOR_BLACK }; int XOrigin, YOrigin; int Amp; int X, Y; unsigned I; - tgi_setpalette (Palette); tgi_setcolor (COLOR_FORE); tgi_clear (); @@ -160,6 +182,9 @@ static void DoDiagram (void) tgi_lineto (XOrigin + X, YOrigin + Y); } + while (kbhit ()) { + cgetc (); + } cgetc (); } @@ -167,11 +192,9 @@ static void DoDiagram (void) 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 (); @@ -182,6 +205,9 @@ static void DoLines (void) tgi_line (Min, Min, Min-X, 0); } + while (kbhit ()) { + cgetc (); + } cgetc (); } @@ -216,10 +242,11 @@ int main (void) Border = bordercolor (COLOR_BLACK); /* Do graphics stuff */ - DoCircles (); - DoCheckerboard (); - DoDiagram (); - DoLines (); + + /* use default palette */ DoCircles (); + DoPalette (0); DoCheckerboard (); + DoPalette (1); DoDiagram (); + DoPalette (2); DoLines (); #if DYN_DRV /* Unload the driver */