From eae262b7c322c2985b22a20fb98a37adfabfcc7e Mon Sep 17 00:00:00 2001 From: uz Date: Tue, 21 Jun 2011 20:34:18 +0000 Subject: [PATCH] NES character mode TGI driver. By Stefan Haubenthal. git-svn-id: svn://svn.cc65.org/cc65/trunk@5067 b7a2c559-68d2-44c3-8de9-860c34a00d81 --- libsrc/Makefile | 1 + libsrc/nes/Makefile | 18 +- libsrc/nes/nes-64-56-2.s | 723 ++++++++++++++++++++++++++++++++++ libsrc/nes/ppu.s | 221 ++++++----- packages/rpm/redhat/cc65.spec | 3 +- 5 files changed, 853 insertions(+), 113 deletions(-) create mode 100644 libsrc/nes/nes-64-56-2.s diff --git a/libsrc/Makefile b/libsrc/Makefile index cb0c0dfa3..62537807c 100644 --- a/libsrc/Makefile +++ b/libsrc/Makefile @@ -309,6 +309,7 @@ neslib: $(AR) a nes.lib $$i/*.o || exit 1; \ done cp nes/*.joy . + cp nes/*.tgi . if [ -d nes/extra ]; then \ for i in nes/extra/*.o; do \ cp $$i nes-`basename $$i` || exit 1; \ diff --git a/libsrc/nes/Makefile b/libsrc/nes/Makefile index dc02356ed..4af6cc227 100644 --- a/libsrc/nes/Makefile +++ b/libsrc/nes/Makefile @@ -41,6 +41,18 @@ CFLAGS = -Osir -g -T -t $(SYS) --forget-inc-paths -I . -I ../../include %.tgi: %.o ../runtime/zeropage.o @$(LD) -o $@ -t module $^ +nes-64-56-2.tgi: nes-64-56-2.o \ + ../runtime/popa.o \ + ../runtime/zeropage.o \ + clrscr.o \ + cputc.o \ + get_tv.o \ + gotoxy.o \ + ppu.o \ + ppubuf.o \ + setcursor.o + @$(LD) -o $@ -t module $^ + #-------------------------------------------------------------------------- # Object files @@ -80,7 +92,7 @@ MOUS = SERS = -TGIS = +TGIS = nes-64-56-2.tgi #-------------------------------------------------------------------------- # Targets @@ -89,6 +101,9 @@ TGIS = all: $(OBJS) $(EMDS) $(JOYS) $(MOUS) $(SERS) $(TGIS) +../runtime/popa.o: + $(MAKE) -C $(dir $@) $(notdir $@) + ../runtime/zeropage.o: $(MAKE) -C $(dir $@) $(notdir $@) @@ -99,3 +114,4 @@ zap: clean @$(RM) $(EMDS) $(JOYS) $(MOUS) $(SERS) $(TGIS) + diff --git a/libsrc/nes/nes-64-56-2.s b/libsrc/nes/nes-64-56-2.s new file mode 100644 index 000000000..deeca5c44 --- /dev/null +++ b/libsrc/nes/nes-64-56-2.s @@ -0,0 +1,723 @@ +; +; Graphics driver for the lores 64x56x2 mode on the NES +; +; Stefan Haubenthal, 2009-03-10 +; Based on Maciej Witkowiak's line routine. +; + + .include "zeropage.inc" + + .include "tgi-kernel.inc" + .include "tgi-error.inc" + .include "nes.inc" + .include "get_tv.inc" + .import _clrscr, setcursor, putchar + .import paldata + + .macpack generic + +; ------------------------------------------------------------------------ +; Header. Includes jump table and constants. + +.segment "JUMPTABLE" + +; 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 +xres: .word charsperline*2 ; Max X resolution +yres: .word 56 ; Max Y resolution + .byte 2 ; Number of drawing colors + .byte 1 ; Number of screens available + .byte 2 ; System font X size + .byte 2 ; System font Y size + .word $100 ; Aspect ratio + +; 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 + .addr 0 ; IRQ entry is unused + +; ------------------------------------------------------------------------ +; 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 +RADIUS = tmp1 + +ADDR = tmp1 +TEMP = tmp3 +TEMP2 = tmp4 +TEMP3 = sreg +TEMP4 = sreg+1 + +; Line routine stuff (must be on zpage) +PB = ptr3 ; (2) LINE +UB = ptr4 ; (2) LINE +ERR = regsave ; (2) LINE +NX = regsave+2 ; (2) LINE + +; Absolute variables used in the code + +.bss + +MEM: .res charsperline*2*56/4 +MEMEND: +ERROR: .res 1 ; Error code +COLOR: .res 1 ; Current color +PALETTE: .res 2 ; The current palette + +; Line routine stuff + +OGora: +COUNT: .res 2 +OUkos: +NY: .res 2 +Y3: +DX: .res 1 +DY: .res 1 +AY: .res 1 + +; Constants and tables + +.rodata + +DEFPALETTE: .byte $0, $1 +OFFSET: .byte 8, 4, 2, 1 +; 00 00 00 00 01 01 01 01 +; 00 01 10 11 00 01 10 11 +CODE: .byte 32, 29, 26, 25, 28, 24+128, 31+128, 30+128 +; 10 10 10 10 11 11 11 11 +; 00 01 10 11 00 01 10 11 + .byte 30, 31, 24, 28+128, 25+128, 26+128, 29+128, 32+128 + +.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: + jsr _get_tv + cmp #TV::NTSC + beq ntsc +; TODO ROM! + inc yres + inc yres +ntsc:; rts + +; ------------------------------------------------------------------------ +; 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: + +; 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: + rts + +; ------------------------------------------------------------------------ +; GETERROR: Return the error code in A and clear it. + +GETERROR: + lda ERROR + ldx #TGI_ERR_OK + 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: + ldx #MEM + stx TEMP+1 + lda #0 + tay +@L1: sta (TEMP),y + iny + bne @L1 + inc TEMP+1 + inx + cpx #>MEMEND + bne @L1 + jmp _clrscr + +; ------------------------------------------------------------------------ +; 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: + sta COLOR +; 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: + +; ------------------------------------------------------------------------ +; 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 + +; ------------------------------------------------------------------------ +; 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: +; Wait for v-blank +@wait: lda PPU_STATUS + bpl @wait + + lda #$3F + sta PPU_VRAM_ADDR2 + lda #$00 + sta PPU_VRAM_ADDR2 + + ldy #0 + lda (ptr1),y + sta PALETTE + tax + lda paldata,x +; sta PPU_VRAM_IO + + iny + lda (ptr1),y + sta PALETTE+1 + tax + lda paldata,x + sta PPU_VRAM_IO + + 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: + ldx Y1 ; y+2=x1) + ldx #X2 + lda X1 + ldy #0 + jsr icmp + bcc @L0243 + beq @L0243 + ; dx = 1; + lda #1 + bne @L0244 + ; else + ; dx = -1; +@L0243: lda #$ff +@L0244: sta DX + ; if (y2>=y1) + ldx #Y2 + lda Y1 + ldy #0 + jsr icmp + bcc @L024A + beq @L024A + ; dy = 1; + lda #1 + bne @L024B + ; else + ; dy = -1; +@L024A: lda #$ff +@L024B: sta DY + ; err = ay = 0; + lda #0 + sta ERR + sta ERR+1 + sta AY + + ; if (nx ny + lda NX + ldx NY + sta NY + stx NX + lda NX+1 + ldx NY+1 + sta NY+1 + stx NX+1 + ; ay = dx + lda DX + sta AY + ; dx = dy = 0; + lda #0 + sta DX + sta DY + ; ny = - ny; +@L0255: lda NY + ldy #0 + jsr neg + sta NY + sty NY+1 + ; for (count=nx;count>0;--count) { + lda NX + ldx #0 + sta COUNT + stx COUNT+1 +@L0166: lda COUNT ; count>0 + ora COUNT+1 + bne @L0167 + rts + ; setpixel(X1,Y1) +@L0167: jsr SETPIXEL + ; pb = err + ny + lda ERR + add NY + sta PB + lda ERR+1 + adc NY+1 + sta PB+1 + tax + ; ub = pb + nx + lda PB + add NX + sta UB + txa + adc NX+1 + sta UB+1 + ; x1 = x1 + dx + ldx #0 + lda DX + bpl @L027B + dex +@L027B: add X1 + sta X1 + txa + adc X1+1 + sta X1+1 + ; y1 = y1 + ay + ldx #0 + lda AY + bpl @L027E + dex +@L027E: add Y1 + sta Y1 + txa + adc Y1+1 + sta Y1+1 + ; if (abs(pb)= 0) && (X1 < XRES) +; (X2 >= 0) && (X2 < XRES) +; (Y1 >= 0) && (Y1 < YRES) +; (Y2 >= 0) && (Y2 < YRES) +; +; Must set an error code: NO +; + +BAR: + inc Y2 +@L1: lda X1 + pha +@L2: jsr SETPIXEL + inc X1 + lda X2 + cmp X1 + bne @L2 + pla + sta X1 + inc Y1 + lda Y2 + cmp Y1 + bne @L1 + 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: + 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: + lda ptr1 + lsr + tay + lda ptr2 + lsr + tax + clc + jsr setcursor + ldy #0 +@L1: lda (ptr3),y + jsr putchar + iny + cmp #$0 + bne @L1 + rts + +;------------- +; copies of some runtime routines + +abs: + ; a/y := abs(a/y) + cpy #0 + bpl absend + ; negay +neg: eor #$ff + add #1 + pha + tya + eor #$ff + adc #0 + tay + pla +absend: rts + +icmp: + ; compare a/y to zp,x + sta TEMP ; TEMP/TEMP2 - arg2 + sty TEMP2 + lda $0,x + pha + lda $1,x + tay + pla + tax + tya ; x/a - arg1 (a=high) + + sub TEMP2 + bne @L4 + cpx TEMP + beq @L3 + adc #$ff + ora #$01 +@L3: rts +@L4: bvc @L3 + eor #$ff + ora #$01 + rts + + +; ------------------------------------------------------------------------ +; Calculate all variables to plot the pixel at X1/Y1. If the point is out +; of range, a carry is returned and INRANGE is set to a value !0 zero. If +; the coordinates are valid, INRANGE is zero and the carry clear. + +CALC: lda xres + sta TEMP + lda #0 + sta TEMP+1 + ldy Y1 +@L1: lda TEMP + add xres + lsr + sta TEMP + lda TEMP+1 + adc #0 + lsr + sta TEMP+1 + dey + bne @L1 + lda TEMP + add X1 + lsr + sta TEMP + lda TEMP+1 + adc #0 + lsr + sta TEMP+1 + lda TEMP + add #MEM + sta TEMP+1 + + lda X1 + and #%00000001 + sta TEMP3 + lda Y1 + asl + and #%00000010 + ora TEMP3 + tax + lda OFFSET,x + rts diff --git a/libsrc/nes/ppu.s b/libsrc/nes/ppu.s index 861685280..344a1071a 100644 --- a/libsrc/nes/ppu.s +++ b/libsrc/nes/ppu.s @@ -4,129 +4,129 @@ ; .export ppuinit - .export paletteinit + .export paletteinit, paldata - .include "nes.inc" + .include "nes.inc" ;+---------+----------------------------------------------------------+ -;| $2000 | PPU Control Register #1 (W) | -;| | | -;| | D7: Execute NMI on VBlank | -;| | 0 = Disabled | -;| | 1 = Enabled | -;| | D6: PPU Master/Slave Selection --+ | -;| | 0 = Master +-- UNUSED | -;| | 1 = Slave --+ | -;| | D5: Sprite Size | -;| | 0 = 8x8 | -;| | 1 = 8x16 | -;| | D4: Background Pattern Table Address | -;| | 0 = $0000 (VRAM) | -;| | 1 = $1000 (VRAM) | -;| | D3: Sprite Pattern Table Address | -;| | 0 = $0000 (VRAM) | -;| | 1 = $1000 (VRAM) | -;| | D2: PPU Address Increment | -;| | 0 = Increment by 1 | -;| | 1 = Increment by 32 | -;| | D1-D0: Name Table Address | -;| | 00 = $2000 (VRAM) | -;| | 01 = $2400 (VRAM) | -;| | 10 = $2800 (VRAM) | -;| | 11 = $2C00 (VRAM) | +;| $2000 | PPU Control Register #1 (W) | +;| | | +;| | D7: Execute NMI on VBlank | +;| | 0 = Disabled | +;| | 1 = Enabled | +;| | D6: PPU Master/Slave Selection --+ | +;| | 0 = Master +-- UNUSED | +;| | 1 = Slave --+ | +;| | D5: Sprite Size | +;| | 0 = 8x8 | +;| | 1 = 8x16 | +;| | D4: Background Pattern Table Address | +;| | 0 = $0000 (VRAM) | +;| | 1 = $1000 (VRAM) | +;| | D3: Sprite Pattern Table Address | +;| | 0 = $0000 (VRAM) | +;| | 1 = $1000 (VRAM) | +;| | D2: PPU Address Increment | +;| | 0 = Increment by 1 | +;| | 1 = Increment by 32 | +;| | D1-D0: Name Table Address | +;| | 00 = $2000 (VRAM) | +;| | 01 = $2400 (VRAM) | +;| | 10 = $2800 (VRAM) | +;| | 11 = $2C00 (VRAM) | ;+---------+----------------------------------------------------------+ ;+---------+----------------------------------------------------------+ -;| $2001 | PPU Control Register #2 (W) | -;| | | -;| | D7-D5: Full Background Colour (when D0 == 1) | -;| | 000 = None +------------+ | -;| | 001 = Green | NOTE: Do not use more | -;| | 010 = Blue | than one type | -;| | 100 = Red +------------+ | -;| | D7-D5: Colour Intensity (when D0 == 0) | -;| | 000 = None +--+ | -;| | 001 = Intensify green | NOTE: Do not use more | -;| | 010 = Intensify blue | than one type | -;| | 100 = Intensify red +--+ | -;| | D4: Sprite Visibility | -;| | 0 = Sprites not displayed | -;| | 1 = Sprites visible | -;| | D3: Background Visibility | -;| | 0 = Background not displayed | -;| | 1 = Background visible | -;| | D2: Sprite Clipping | -;| | 0 = Sprites invisible in left 8-pixel column | -;| | 1 = No clipping | -;| | D1: Background Clipping | -;| | 0 = BG invisible in left 8-pixel column | -;| | 1 = No clipping | -;| | D0: Display Type | -;| | 0 = Colour display | -;| | 1 = Monochrome display | +;| $2001 | PPU Control Register #2 (W) | +;| | | +;| | D7-D5: Full Background Colour (when D0 == 1) | +;| | 000 = None +------------+ | +;| | 001 = Green | NOTE: Do not use more | +;| | 010 = Blue | than one type | +;| | 100 = Red +------------+ | +;| | D7-D5: Colour Intensity (when D0 == 0) | +;| | 000 = None +--+ | +;| | 001 = Intensify green | NOTE: Do not use more | +;| | 010 = Intensify blue | than one type | +;| | 100 = Intensify red +--+ | +;| | D4: Sprite Visibility | +;| | 0 = Sprites not displayed | +;| | 1 = Sprites visible | +;| | D3: Background Visibility | +;| | 0 = Background not displayed | +;| | 1 = Background visible | +;| | D2: Sprite Clipping | +;| | 0 = Sprites invisible in left 8-pixel column | +;| | 1 = No clipping | +;| | D1: Background Clipping | +;| | 0 = BG invisible in left 8-pixel column | +;| | 1 = No clipping | +;| | D0: Display Type | +;| | 0 = Colour display | +;| | 1 = Monochrome display | ;+---------+----------------------------------------------------------+ ;----------------------------------------------------------------------------- -.proc ppuinit +.proc ppuinit - lda #%10101000 - sta PPU_CTRL1 + lda #%10101000 + sta PPU_CTRL1 - lda #%00011110 - sta PPU_CTRL2 + lda #%00011110 + sta PPU_CTRL2 ; Wait for vblank -@wait: lda PPU_STATUS - bpl @wait +@wait: lda PPU_STATUS + bpl @wait ; reset scrolling - lda #0 - sta PPU_VRAM_ADDR1 - sta PPU_VRAM_ADDR1 + lda #0 + sta PPU_VRAM_ADDR1 + sta PPU_VRAM_ADDR1 ; Make all sprites invisible - lda #$00 - ldy #$f0 - sta PPU_SPR_ADDR - ldx #$40 -@loop: sty PPU_SPR_IO - sta PPU_SPR_IO - sta PPU_SPR_IO - sty PPU_SPR_IO - dex - bne @loop + lda #$00 + ldy #$f0 + sta PPU_SPR_ADDR + ldx #$40 +@loop: sty PPU_SPR_IO + sta PPU_SPR_IO + sta PPU_SPR_IO + sty PPU_SPR_IO + dex + bne @loop - rts + rts .endproc ;----------------------------------------------------------------------------- -.proc paletteinit +.proc paletteinit ; Wait for v-blank -@wait: lda PPU_STATUS - bpl @wait +@wait: lda PPU_STATUS + bpl @wait - lda #$3F - sta PPU_VRAM_ADDR2 - lda #$00 - sta PPU_VRAM_ADDR2 + lda #$3F + sta PPU_VRAM_ADDR2 + lda #$00 + sta PPU_VRAM_ADDR2 - ldx #0 -@loop: lda paldata,x - sta PPU_VRAM_IO + ldx #0 +@loop: lda paldata,x + sta PPU_VRAM_IO inx - cpx #(16*2) - bne @loop + cpx #(16*2) + bne @loop - rts - + rts + .endproc ;----------------------------------------------------------------------------- @@ -134,25 +134,24 @@ .rodata paldata: - .repeat 2 - .byte $0f ; 0 black - .byte $14 ; 4 violett - .byte $3b ; 3 cyan - .byte $3d ; 1 white - - .byte $38 ; 7 yellow - .byte $2d ; b dark grey - .byte $22 ; e light blue - .byte $04 ; 2 red - - .byte $18 ; 8 orange - .byte $08 ; 9 brown - .byte $35 ; a light red - .byte $01 ; 6 blue - - .byte $10 ; c middle grey - .byte $2b ; d light green - .byte $3d ; f light gray - .byte $1a ; 5 green - .endrepeat - + .repeat 2 + .byte $0f ; 0 black + .byte $14 ; 4 violett + .byte $3b ; 3 cyan + .byte $3d ; 1 white + + .byte $38 ; 7 yellow + .byte $2d ; b dark grey + .byte $22 ; e light blue + .byte $04 ; 2 red + + .byte $18 ; 8 orange + .byte $08 ; 9 brown + .byte $35 ; a light red + .byte $01 ; 6 blue + + .byte $10 ; c middle grey + .byte $2b ; d light green + .byte $3d ; f light gray + .byte $1a ; 5 green + .endrepeat diff --git a/packages/rpm/redhat/cc65.spec b/packages/rpm/redhat/cc65.spec index 457763b72..c662a4243 100644 --- a/packages/rpm/redhat/cc65.spec +++ b/packages/rpm/redhat/cc65.spec @@ -354,7 +354,7 @@ programs for the Lynx Game Console using the cc65 crosscompiler. %attr(644,root,root) %{_libdir}/cc65/ser/lynx-*.ser %attr(644,root,root) %{_libdir}/cc65/tgi/lynx-*.tgi %attr(644,root,root) %{_libdir}/cc65/cfg/lynx-*.cfg - + ############################################################################### @@ -375,6 +375,7 @@ crosscompiler. %files nes %attr(644,root,root) %{_libdir}/cc65/lib/nes.lib %attr(644,root,root) %{_libdir}/cc65/joy/nes-*.joy +%attr(644,root,root) %{_libdir}/cc65/tgi/nes-*.tgi -- 2.39.5