--- /dev/null
+;
+; Startup code for cc65 (NES version)
+;
+; by Groepaz/Hitmen <groepaz@gmx.net>
+; based on code by Ullrich von Bassewitz <uz@cc65.org>
+;
+; This must be the *first* file on the linker command line
+;
+
+ .export _exit
+ .import initlib, donelib
+ .import push0, _main, zerobss
+ .import ppubuf_flush
+
+ ; Linker generated symbols
+ .import __RAM_START__, __RAM_SIZE__
+ .import __SRAM_START__, __SRAM_SIZE__
+ .import __ROM0_START__, __ROM0_SIZE__
+ .import __STARTUP_LOAD__,__STARTUP_RUN__, __STARTUP_SIZE__
+ .import __CODE_LOAD__,__CODE_RUN__, __CODE_SIZE__
+ .import __RODATA_LOAD__,__RODATA_RUN__, __RODATA_SIZE__
+ .import __DATA_LOAD__,__DATA_RUN__, __DATA_SIZE__
+
+ .include "zeropage.inc"
+ .include "nes.inc"
+
+
+; ------------------------------------------------------------------------
+; 16 bytes INES header
+
+.segment "HEADER"
+
+; +--------+------+------------------------------------------+
+; | Offset | Size | Content(s) |
+; +--------+------+------------------------------------------+
+; | 0 | 3 | 'NES' |
+; | 3 | 1 | $1A |
+; | 4 | 1 | 16K PRG-ROM page count |
+; | 5 | 1 | 8K CHR-ROM page count |
+; | 6 | 1 | ROM Control Byte #1 |
+; | | | %####vTsM |
+; | | | | ||||+- 0=Horizontal mirroring |
+; | | | | |||| 1=Vertical mirroring |
+; | | | | |||+-- 1=SRAM enabled |
+; | | | | ||+--- 1=512-byte trainer present |
+; | | | | |+---- 1=Four-screen mirroring |
+; | | | | | |
+; | | | +--+----- Mapper # (lower 4-bits) |
+; | 7 | 1 | ROM Control Byte #2 |
+; | | | %####0000 |
+; | | | | | |
+; | | | +--+----- Mapper # (upper 4-bits) |
+; | 8-15 | 8 | $00 |
+; | 16-.. | | Actual 16K PRG-ROM pages (in linear |
+; | ... | | order). If a trainer exists, it precedes |
+; | ... | | the first PRG-ROM page. |
+; | ..-EOF | | CHR-ROM pages (in ascending order). |
+; +--------+------+------------------------------------------+
+
+ .byte $4e,$45,$53,$1a ; "nes\n"
+ .byte 2 ; ines prg - Specifies the number of 16k prg banks.
+ .byte 1 ; ines chr - Specifies the number of 8k chr banks.
+ .byte %00000011 ; ines mir - Specifies VRAM mirroring of the banks.
+ .byte %00000000 ; ines map - Specifies the NES mapper used.
+ .byte 0,0,0,0,0,0,0,0 ; 8 zeroes
+
+
+; ------------------------------------------------------------------------
+; Create an empty LOWCODE segment to avoid linker warnings
+
+.segment "LOWCODE"
+
+; ------------------------------------------------------------------------
+; Place the startup code in a special segment.
+
+.segment "STARTUP"
+
+start:
+
+; setup the CPU and System-IRQ
+
+ sei
+ cld
+ ldx #0
+ stx VBLANK_FLAG
+
+ stx ringread
+ stx ringwrite
+ stx ringcount
+
+ txs
+
+ lda #$20
+@l: sta ringbuff,x
+ sta ringbuff+$0100,x
+ sta ringbuff+$0200,x
+ inx
+ bne @l
+
+; Clear the BSS data
+
+ jsr zerobss
+
+; Copy the .data segment to RAM
+
+ lda #<(__ROM0_START__ + __STARTUP_SIZE__+ __CODE_SIZE__+ __RODATA_SIZE__)
+ sta ptr1
+ lda #>(__ROM0_START__ + __STARTUP_SIZE__+ __CODE_SIZE__+ __RODATA_SIZE__)
+ sta ptr1+1
+ lda #<(__DATA_RUN__)
+ sta ptr2
+ lda #>(__DATA_RUN__)
+ sta ptr2+1
+
+ ldx #>(__DATA_SIZE__)
+
+@l2: beq @s1 ; no more full pages
+
+ ; copy one page
+ ldy #0
+@l1: lda (ptr1),y
+ sta (ptr2),y
+ iny
+ bne @l1
+
+ inc ptr1+1
+ inc ptr2+1
+ dex
+ bne @l2
+
+ ; copy remaining bytes
+@s1:
+
+ ; copy one page
+ ldy #0
+@l3: lda (ptr1),y
+ sta (ptr2),y
+ iny
+ cpy #<(__DATA_SIZE__)
+ bne @l3
+
+; setup the stack
+
+ lda #<(__SRAM_START__ + __SRAM_SIZE__)
+ sta sp
+ lda #>(__SRAM_START__ + __SRAM_SIZE__)
+ sta sp+1 ; Set argument stack ptr
+
+; Call module constructors
+
+ jsr initlib
+
+; Push arguments and call main()
+
+ jsr callmain
+
+; Call module destructors. This is also the _exit entry.
+
+_exit: jsr donelib ; Run module destructors
+
+; Reset the NES
+
+ jmp start
+
+; ------------------------------------------------------------------------
+; System V-Blank Interupt
+; updates PPU Memory (buffered)
+; updates VBLANK_FLAG and _tickcount
+; ------------------------------------------------------------------------
+
+nmi: pha
+ tya
+ pha
+ txa
+ pha
+
+ lda #1
+ sta VBLANK_FLAG
+
+ inc _tickcount
+ bne @s
+ inc _tickcount+1
+
+@s: jsr ppubuf_flush
+
+ ; reset the video counter
+ lda #$20
+ sta PPU_VRAM_ADDR2
+ lda #$00
+ sta PPU_VRAM_ADDR2
+
+ ; reset scrolling
+ sta PPU_VRAM_ADDR1
+ sta PPU_VRAM_ADDR1
+
+ pla
+ tax
+ pla
+ tay
+ pla
+
+; Interrupt exit
+
+irq2:
+irq1:
+timerirq:
+irq:
+ rti
+
+; ------------------------------------------------------------------------
+; hardware vectors
+; ------------------------------------------------------------------------
+
+.segment "VECTORS"
+
+ .word irq2 ; $fff4 ?
+ .word irq1 ; $fff6 ?
+ .word timerirq ; $fff8 ?
+ .word nmi ; $fffa vblank nmi
+ .word start ; $fffc reset
+ .word irq ; $fffe irq / brk
+
+; ------------------------------------------------------------------------
+; character data
+; ------------------------------------------------------------------------
+
+.segment "CHARS"
+
+ .incbin "nes/neschar.bin"
+
+
--- /dev/null
+;
+; NES definitions. By Groepaz/Hitmem.
+;
+
+
+;; FIXME: optimize zeropage usage
+
+SCREEN_PTR = $62 ;2
+CRAM_PTR = $64 ;2
+CHARCOLOR = $66
+BGCOLOR = $67
+RVS = $68
+CURS_X = $69
+CURS_Y = $6a
+
+_tickcount = $6b ;2
+
+VBLANK_FLAG = $70
+
+ringbuff = $0200
+ringwrite = $71
+ringread = $72
+ringcount = $73
+
+ppuhi = $74
+ppulo = $75
+ppuval = $76
+
+screenrows = (32-4)
+charsperline = 32
+xsize = charsperline
+
+;; PPU defines
+
+PPU_CTRL1 = $2000
+PPU_CTRL2 = $2001
+PPU_STATUS = $2002
+PPU_SPR_ADDR = $2003
+PPU_SPR_IO = $2004
+PPU_VRAM_ADDR1 = $2005
+PPU_VRAM_ADDR2 = $2006
+PPU_VRAM_IO = $2007
+
+;; APU defines
+
+APU_PULSE1CTRL = $4000 ; Pulse #1 Control Register (W)
+APU_PULSE1RAMP = $4001 ; Pulse #1 Ramp Control Register (W)
+APU_PULSE1FTUNE = $4002 ; Pulse #1 Fine Tune (FT) Register (W)
+APU_PULSE1CTUNE = $4003 ; Pulse #1 Coarse Tune (CT) Register (W)
+APU_PULSE2CTRL = $4004 ; Pulse #2 Control Register (W)
+APU_PULSE2RAMP = $4005 ; Pulse #2 Ramp Control Register (W)
+APU_PULSE2FTUNE = $4006 ; Pulse #2 Fine Tune Register (W)
+APU_PULSE2STUNE = $4007 ; Pulse #2 Coarse Tune Register (W)
+APU_TRICTRL1 = $4008 ; Triangle Control Register #1 (W)
+APU_TRICTRL2 = $4009 ; Triangle Control Register #2 (?)
+APU_TRIFREQ1 = $400A ; Triangle Frequency Register #1 (W)
+APU_TRIFREQ2 = $400B ; Triangle Frequency Register #2 (W)
+APU_NOISECTRL = $400C ; Noise Control Register #1 (W)
+;;APU_ = $400D ; Unused (???)
+APU_NOISEFREQ1 = $400E ; Noise Frequency Register #1 (W)
+APU_NOISEFREQ2 = $400F ; Noise Frequency Register #2 (W)
+APU_MODCTRL = $4010 ; Delta Modulation Control Register (W)
+APU_MODDA = $4011 ; Delta Modulation D/A Register (W)
+APU_MODADDR = $4012 ; Delta Modulation Address Register (W)
+APU_MODLEN = $4013 ; Delta Modulation Data Length Register (W)
+APU_SPR_DMA = $4014 ; Sprite DMA Register (W)
+APU_CHANCTRL = $4015 ; Sound/Vertical Clock Signal Register (R)
+APU_PAD1 = $4016 ; Joypad #1 (RW)
+APU_PAD2 = $4017 ; Joypad #2/SOFTCLK (RW)
+
+
+CH_HLINE = 11
+CH_VLINE = 14
+CH_ULCORNER = 176
+CH_URCORNER = 174
+CH_LLCORNER = 173
+CH_LRCORNER = 189
+CH_TTEE = 178
+CH_RTEE = 179
+CH_BTEE = 177
+CH_LTEE = 171
+CH_CROSS = 123
+CH_CURS_UP = 145
+CH_CURS_DOWN = 17
+CH_CURS_LEFT = 157
+CH_CURS_RIGHT = 29
+CH_PI = 126
+CH_DEL = 20
+CH_INS = 148
+CH_ENTER = 10
+CH_STOP = 3
+CH_ESC = 27
+