Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- ;shared version 0
- .FEATURE c_comments
- .import _main
- ;boilerplate stuff and linker gen. symbols
- .export __STARTUP__ : absolute = 1 ; Mark as startup
- .import __RAM_START__, __RAM_SIZE__
- .include "zeropage.inc"
- ;-MACROS-
- ;increment 16-bit value
- .macro inc16 address
- .local skip
- inc address
- bne skip
- inc address + 1
- skip:
- .endmacro
- ;decrement 16-bit value
- .macro dec16 address
- .local skip
- dec address
- bne skip
- dec address+1
- skip:
- .endmacro
- ;far branches
- ;carry flag
- .macro bcc_far address
- bcs * + 5
- jmp address
- .endmacro
- .macro bcs_far address
- bcc * + 5
- jmp address
- .endmacro
- ;zero flag
- .macro beq_far address
- bne * + 5
- jmp address
- .endmacro
- .macro bne_far address
- beq * + 5
- jmp address
- .endmacro
- ;negative flag
- .macro bmi_far address
- bpl * + 5
- jmp address
- .endmacro
- .macro bpl_far address
- bmi * + 5
- jmp address
- .endmacro
- ;overflow flag
- .macro bvc_far address
- bvs * + 5
- jmp address
- .endmacro
- .macro bvs_far address
- bvc * + 5
- jmp address
- .endmacro
- ;loads 16-bit number into a 16-bit register
- .macro LoadWord value, low_byte,high_byte
- lda #<value
- sta low_byte
- lda #>value
- sta high_byte
- .endmacro
- ;loads 16-bit number into a 16-bit register (in big endian)
- .macro LoadWordRev value, low_byte, high_byte
- lda #>value
- sta low_byte
- lda #<value
- sta high_byte
- .endmacro
- ;-GLOBAL LABELS-
- ;-ram oriented-
- nameTableBuffer = $0100 ;->019F; 160 bytes
- stackStart = $01A0 ;->01FF; 96 bytes
- ;-hardware oriented-
- CONTROLLER1 = $4016
- JOYPAD1 = $4016 ;alias
- CONTROLLER2 = $4017
- JOYPAD2 = $4017
- ;ppu registers
- PPUCTRL = $2000 ;> write
- ;7 bit 0
- ;---- ----
- ;VPHB SINN
- ;|||| ||||
- ;|||| ||++- Base nametable address
- ;|||| || (0 = $2000; 1 = $2400; 2 = $2800; 3 = $2C00)
- ;|||| |+--- VRAM address increment per CPU read/write of PPUDATA
- ;|||| | (0: add 1, going across; 1: add 32, going down)
- ;|||| +---- Sprite pattern table address for 8x8 sprites
- ;|||| (0: $0000; 1: $1000; ignored in 8x16 mode)
- ;|||+------ Background pattern table address (0: $0000; 1: $1000)
- ;||+------- Sprite size (0: 8x8 pixels; 1: 8x16 pixels – see PPU OAM#Byte 1)
- ;|+-------- PPU master/slave select
- ;| (0: read backdrop from EXT pins; 1: output color on EXT pins)
- ;+--------- Generate an NMI at the start of the
- ; vertical blanking interval (0: off; 1: on)
- PPUMASK = $2001 ;> write
- ;7 bit 0
- ;---- ----
- ;BGRs bMmG
- ;|||| ||||
- ;|||| |||+- Greyscale (0: normal color, 1: produce a greyscale display)
- ;|||| ||+-- 1: Show background in leftmost 8 pixels of screen, 0: Hide
- ;|||| |+--- 1: Show sprites in leftmost 8 pixels of screen, 0: Hide
- ;|||| +---- 1: Show background
- ;|||+------ 1: Show sprites
- ;||+------- Emphasize red (green on PAL/Dendy)
- ;|+-------- Emphasize green (red on PAL/Dendy)
- ;+--------- Emphasize blue
- PPUSTATUS = $2002 ;< read
- OAMADDR = $2003 ;> write
- OAMDATA = $2004 ;<> read/write
- PPUSCROLL = $2005 ;>> write 2x
- PPUADDR = $2006 ;>> write 2x
- PPUDATA = $2007 ;<> read/write
- OAMDMA = $4014 ;> write (do this during vblank)
- ;palette stuff (there are 25 total palette colors registers)
- PPUPAL_BGUNI = $3F00 ; universal background color
- PPUPAL_BG0 = $3F01 ;->$3F03 background palette 0
- PPUPAL_BG0_A = PPUPAL_BG0
- PPUPAL_BG0_B = PPUPAL_BG0 + 1
- PPUPAL_BG0_C = PPUPAL_BG0 + 2
- PPUPAL_BG1 = $3F05 ;->$3F07 background palette 1
- PPUPAL_BG1_A = PPUPAL_BG1
- PPUPAL_BG1_B = PPUPAL_BG1 + 1
- PPUPAL_BG1_C = PPUPAL_BG1 + 2
- PPUPAL_BG2 = $3F09 ;->$3F0B background palette 2
- PPUPAL_BG2_A = PPUPAL_BG2
- PPUPAL_BG2_B = PPUPAL_BG2 + 1
- PPUPAL_BG2_C = PPUPAL_BG2 + 2
- PPUPAL_BG3 = $3F0D ;->$3F0F background palette 3
- PPUPAL_BG3_A = PPUPAL_BG3
- PPUPAL_BG3_B = PPUPAL_BG3 + 1
- PPUPAL_BG3_C = PPUPAL_BG3 + 2
- PPUPAL_SP0 = $3F10 ;->$3F13 sprite palette 0
- PPUPAL_SP0_A = PPUPAL_SP0
- PPUPAL_SP0_B = PPUPAL_SP0 + 1
- PPUPAL_SP0_C = PPUPAL_SP0 + 2
- PPUPAL_SP1 = $3F14 ;->$3F17 sprite palette 1
- PPUPAL_SP1_A = PPUPAL_SP1
- PPUPAL_SP1_B = PPUPAL_SP1 + 1
- PPUPAL_SP1_C = PPUPAL_SP1 + 2
- PPUPAL_SP2 = $3F18 ;->$3F1B sprite palette 2
- PPUPAL_SP2_A = PPUPAL_SP2
- PPUPAL_SP2_B = PPUPAL_SP2 + 1
- PPUPAL_SP2_C = PPUPAL_SP2 + 2
- PPUPAL_SP3 = $3F1C ;->$3F1F sprite palette 3
- PPUPAL_SP3_A = PPUPAL_SP3
- PPUPAL_SP3_B = PPUPAL_SP3 + 1
- PPUPAL_SP3_C = PPUPAL_SP3 + 2
- ;basic html color names with aliases (ntsc grayscale=$0d,$2d,$00,$10,$3d,$30)
- PPUPAL_WHITE = $30 ;#$FFFFFF
- PPUPAL_SILVER = $10 ;#$C0C0C0
- PPUPAL_GRAYHI = $10 ;^^
- PPUPAL_GRAY = $00 ;#$808080
- PPUPAL_GRAYLO = $00 ;^^
- PPUPAL_BLACK = $0D ;#$000000
- PPUPAL_RED = $16 ;#$FF0000
- PPUPAL_REDHI = $16 ;^^
- PPUPAL_MAROON = $06 ;#$800000
- PPUPAL_REDLO = $06 ;^^
- PPUPAL_YELLOW = $37 ;#$FFFF00 (not that useful for ntsc)
- PPUPAL_YELLOWHI = $37 ;^^
- PPUPAL_OLIVE = $28 ;#$808000
- PPUPAL_YELLOWLO = $28 ;^^
- PPUPAL_LIME = $2a ;#$00FF00
- PPUPAL_GREENHI = $2a ;^^
- PPUPAL_GREEN = $1a ;#$008000
- PPUPAL_GREENLO = $1a ;^^
- PPUPAL_AQUA = $2c ;#$00FFFF
- PPUPAL_CYANHI = $2c ;^^
- PPUPAL_TEAL = $1c ;#$008080
- PPUPAL_CYANLO = $1c ;^^
- PPUPAL_BLUE = $12 ;#$0000FF
- PPUPAL_BLUEHI = $12 ;^^
- PPUPAL_NAVY = $01 ;#$000080
- PPUPAL_BLUELO = $01 ;^^
- PPUPAL_FUCHSIA = $24 ;#$FF00FF
- PPUPAL_MAGENTAHI = $24 ;^^
- PPUPAL_PURPLE = $04 ;#$800080
- PPUPAL_MAGENTALO = $04 ;^^
- ;$0d = 00 = 0
- ;$2d = 4e = 78
- ;$00 = 65 = 101
- ;$10 = ae = 174
- ;$3d = b7 = 183
- ;$30 = ff = 255
- ;nametable stuff
- PPU_NAMETABLE_A = $2000
- PPU_NAMETABLE_B = $2400
- PPU_NAMETABLE_C = $2800
- PPU_NAMETABLE_D = $2C00
- ;attribute table byte layout:
- ;7654 3210
- ;|||| ||++- Color bits 3-2 for top left quadrant of this byte
- ;|||| ++--- Color bits 3-2 for top right quadrant of this byte
- ;||++------ Color bits 3-2 for bottom left quadrant of this byte
- ;++-------- Color bits 3-2 for bottom right quadrant of this byte
- PPU_ATTRTABLE0 = $23C0
- PPU_ATTRTABLE1 = $27C0
- PPU_ATTRTABLE2 = $2BC0
- PPU_ATTRTABLE3 = $2FC0
- ;apu registers (</> xxxx xxxx = read/write bits7654 3210)
- ;Pulse 1 channel (> write)
- APU_PULSE1_0 = $4000 ;> DDLC.NNNN Duty, loop envelope/disable length counter, constant volume, envelope period/volume
- APU_PULSE1_1 = $4001 ;> EPPP.NSSS Sweep unit: enabled, period, negative, shift count
- APU_PULSE1_2 = $4002 ;> LLLL.LLLL Timer low
- APU_PULSE1_3 = $4003 ;> LLLL.LHHH Length counter load, timer high (also resets duty and starts envelope)
- ;Pulse 2 channel (> write)
- APU_PULSE2_0 = $4004 ;> DDLC.NNNN Duty, loop envelope/disable length counter, constant volume, envelope period/volume
- APU_PULSE2_1 = $4005 ;> EPPP.NSSS Sweep unit: enabled, period, negative, shift count
- APU_PULSE2_2 = $4006 ;> LLLL.LLLL Timer low
- APU_PULSE2_3 = $4007 ;> LLLL.LHHH Length counter load, timer high (also resets duty and starts envelope)
- ;Triangle channel (> write)
- APU_TRIANGLE_0 = $4008 ;> CRRR.RRRR Length counter disable/linear counter control, linear counter reload value
- APU_TRIANGLE_1 = $400A ;> LLLL.LLLL Timer low
- APU_TRIANGLE_2 = $400B ;> LLLL.LHHH Length counter load, timer high (also reloads linear counter)
- ;Noise channel (> write)
- APU_NOISE_0 = $400C ;> --LC.NNNN Loop envelope/disable length counter, constant volume, envelope period/volume
- APU_NOISE_1 = $400E ;> L---.PPPP Loop noise, noise period
- APU_NOISE_2 = $400F ;> LLLL.L--- Length counter load (also starts envelope)
- ;DMC channel (> write)
- APU_DMC_CONTROL = $4010 ;> IL--.FFFF IRQ enable, loop sample, frequency index
- APU_DMC_DIRECT_LOAD = $4011 ;> -DDD.DDDD Direct load
- APU_DMC_ADDRESS = $4012 ;> AAAA.AAAA Sample address %11AAAAAA.AA000000
- APU_DMC_LENGTH = $4013 ;> LLLL.LLLL Sample length %0000LLLL.LLLL0001
- ;general registers
- APU_CONTROL = $4015 ;> ---D NT21 Control: DMC enable, length counter enables: noise, triangle, pulse 2, pulse 1 (> write)
- APU_STATUS = $4015 ;< IF-D NT21 Status: DMC interrupt, frame interrupt, length counter status: noise, triangle, pulse 2, pulse 1 (< read)
- APU_FRAME_COUNTER = $4017 ;> SD-- ---- Frame counter: 5-frame sequence, disable frame interrupt (> write)
- .segment "HEADER"
- ;iNES header for nrom mapper
- .byte "NES",$1a ; Constant; "File signature,MS-DOS EOF"
- .byte $02 ; Size of PRG ROM in 16kB units (1 or 2 for NROM-128 or NROM-256 respectively)
- .byte $01 ; Same as above, but for CHR ROM in 8kB units
- .byte %00000001 ; Flags 6 (see below); mapper 0, vertical mirroring
- .byte %00000000 ; Flags 7 (see below); mapper 0
- .byte $00 ; Flags 8 (see below)
- .byte $00 ; Flags 9 (see below)
- .byte $00 ; Flags 10 (see below)
- .byte 0,0,0,0,0 ; Padding/unused bytes
- ;No trainer is used, so no bytes need to be added (or is that an nes2.0 feature? I forget)
- ;FLAGS 6:
- ;========
- ;76543210
- ;||||||||
- ;|||||||+- Mirroring: 0: horizontal (vertical arrangement) (CIRAM A10 = PPU A11)
- ;||||||| 1: vertical (horizontal arrangement) (CIRAM A10 = PPU A10)
- ;||||||+-- 1: Cartridge contains battery-backed PRG RAM ($6000-7FFF) or other persistent memory
- ;|||||+--- 1: 512-byte trainer at $7000-$71FF (stored before PRG data)
- ;||||+---- 1: Ignore mirroring control or above mirroring bit; instead provide four-screen VRAM
- ;++++----- Lower nybble of mapper number
- ;FLAGS 7:
- ;========
- ;76543210
- ;||||||||
- ;|||||||+- VS Unisystem
- ;||||||+-- PlayChoice-10 (8KB of Hint Screen data stored after CHR data)
- ;||||++--- If equal to 2, flags 8-15 are in NES 2.0 format
- ;++++----- Upper nybble of mapper number
- ;FLAGS 8:
- ;76543210
- ;||||||||
- ;++++++++- PRG RAM size
- ;FLAGS 9:
- ;76543210
- ;||||||||
- ;|||||||+- TV system (0: NTSC; 1: PAL)
- ;+++++++-- Reserved, set to zero
- ;FLAGS 10:
- ;76543210
- ; || ||
- ; || ++- TV system (0: NTSC; 2: PAL; 1/3: dual compatible)
- ; |+----- PRG RAM ($6000-$7FFF) (0: present; 1: not present)
- ; +------ 0: Board has no bus conflicts; 1: Board has bus conflicts
- .segment "ZEROPAGE"
- .export _fcounterTOT,_fcounterNLG,_fcounterLAG
- .export _joypadState,_joypadState1,_joypadState2
- .export _randomNum,_randomNuml,_randomNumh
- .export _fstatus,_ppuctrl,_ppumask
- .export _scroll,_scrollX,_scrollY
- scratch: .res 13
- fcounter: .res 3 ;global frame counters
- _fcounterTOT = fcounter ;^^total frames (0-> 59)
- _fcounterNLG = fcounter+1 ;^^non-lag frames (0-> 59)
- _fcounterLAG = fcounter+2 ;^^lag frames (0->255)
- r16a: .res 3 ;register 16 a
- r16al = r16a ;^^
- r16ah = r16a+1 ;^^
- swap = r16a+2 ;^^(very) temporary scratch space
- r16b: .res 2 ;register 16 b
- r16bl = r16b ;^^
- r16bh = r16b+1 ;^^
- r16c: .res 2 ;register 16 c
- r16cl = r16c ;^^
- r16ch = r16c+1 ;^^
- _joypadState: .res 2 ;last controller reads
- _joypadState1 = _joypadState ;^^
- _joypadState2 = _joypadState+1 ;^^
- _randomNum: .res 2 ;current pseudorandomly generated number
- _randomNuml = _randomNum ;^^
- _randomNumh = _randomNum+1 ;^^
- ;frame status:
- ;bit 7=main loop finished?
- ;6=do nametable upload?
- _fstatus: .res 1
- _ppuctrl: .res 1 ;copies of ppu registers (which are normally write only)
- _ppumask: .res 1 ;^^
- _scroll: .res 2 ;scroll state
- _scrollX = _scroll ;^^
- _scrollY = _scroll+1 ;^^
- ;i couldn't get OAMBuffer to just be = to 0x200 in c
- ;so now these are all a #define fight me
- .segment "NAMETABLE_BUF"
- ;.export _nametableBuffer
- _nametableBuffer: .res 160
- .segment "OAM_BUF"
- ;.export _OAMBuffer
- _OAMBuffer: .res 256 ;OAM_Entry* (typedef struct array)
- ;//attributes:
- ;//76543210
- ;//||||||||
- ;//||||||++- Palette (4 to 7) of sprite
- ;//|||+++--- Unimplemented (read 0)
- ;//||+------ Priority (0: in front of background; 1: behind background)
- ;//|+------- Flip sprite horizontally
- ;//+-------- Flip sprite vertically
- .segment "APU_BUF"
- ;.export _APUBuffer
- _APUBuffer: .res 256
- .segment "BSS"
- ;.export _highMemory
- ;_highMemory: .res 768
- ;1 page for cc65 param stack
- .segment "STARTUP"
- .export _reset ;void (void)
- _reset:
- sei ;enable 'ignore irq?' bit
- cld ;clear decimal mode
- ldx #%01000000 ;disable apu frame irq
- stx APU_FRAME_COUNTER ;^^
- ldx #$ff ;set up stack
- txs ;^^
- inx ;x=0
- stx PPUCTRL ;disable nmi
- stx PPUMASK ;disable rendering (start fblank)
- stx APU_DMC_CONTROL ;disable dmc irqs
- bit PPUSTATUS ;init vblank flag to known state
- jsr _spin ;wait for 1st vblank
- @clearMemoryLoop: ;flush ram
- lda #0 ;^^
- sta $0000,x ;^^
- sta $0100,x ;^^
- ;sta $0200,x ;^^(oam buffer handled separately)
- sta $0300,x ;^^
- sta $0400,x ;^^
- sta $0500,x ;^^
- sta $0600,x ;^^
- sta $0700,x ;^^
- lda #$ff ;^^#$ff for oam so that things start off screen
- sta _OAMBuffer,x ;^^
- inx ;^^
- bne @clearMemoryLoop ;^^
- stx OAMADDR ;OAMADDR=0 bc i use OAMDMA instead
- lda #<(__RAM_START__ + __RAM_SIZE__ -1) ;set cc65 argument stack pointer
- sta sp ;^^
- lda #>(__RAM_START__ + __RAM_SIZE__ -1) ;^^(remove -1 if things go wack)
- sta sp+1 ;^^
- lda #$aa ;seed rng
- sta _randomNuml ;^^
- sta _randomNumh ;^^
- lda #%10000000 ;initialize frame status to 'finished'
- sta _fstatus ;^^
- jsr _spin ;wait for 2nd vsync
- lda #%00011110 ;enable sprites & background (stop fblank)
- sta _ppumask ;^^
- sta PPUMASK ;^^
- lda #%10010000 ;enable nmi; patt. table: sprite=left, background=right
- sta _ppuctrl ;^^
- sta PPUCTRL ;^^
- cli ;reenable irq
- jmp _main ;jump to c main
- .segment "CODE"
- .export _vblank,_spin
- .export _toIntS,_toIntU
- .export _random8,_random16
- .export _getButtons
- .export _putPalBackground,_putPalSprite
- .export _cos,_sin
- _vblank:
- lda #0 ;for zeroing out frame counters
- ldx #60 ;for frame compares
- inc _fcounterTOT ;inc total frames count and/or reset
- cpx _fcounterTOT ;^^
- bne @incFCountTOT ;^^
- sta _fcounterTOT ;^^reset if 60 frames elapsed since last reset
- @incFCountTOT:
- bit _fstatus ;if game loop hasn't finished, exit nmi early
- bmi @doVBlank ;^^(game loop finished = bit 7 true)
- inc _fcounterLAG ;inc amount of lag frames (this can roll over lol)
- jmp @skipVBlank ;exit nmi
- @doVBlank:
- sta _fcounterLAG ;game loop finished before nmi; reset lag frames
- inc _fcounterNLG ;inc no lag frames count and/or reset
- cpx _fcounterNLG ;^^
- bne @incFCountNLG ;^^
- sta _fcounterNLG ;^^reset if 60 frames elapsed since last reset
- lda _fstatus ;reset 'main loop finished' flag
- and #%01111111 ;^^
- sta _fstatus ;^^
- @incFCountNLG:
- sta _fcounterLAG ;reset lag frames
- lda _ppumask ;start fblank
- and #%11100111 ;^^
- sta _ppumask ;^^
- sta PPUMASK ;^^
- ;upload things to vram
- bit _fstatus
- bvc @skipNametableUpload ;if bit 6 of _fstatus set, upload nametable
- ;do nametable upload stuff here
- @skipNametableUpload:
- lda #>_OAMBuffer ;upload OAMBuffer to actual OAM
- sta OAMDMA ;^^
- lda _ppuctrl ;send ppuctrl's new state to the real ppu register
- sta PPUCTRL ;^^
- lda _ppumask ;stop fblank
- ora #%00011000 ;^^
- sta _ppumask ;^^
- sta PPUMASK ;^^
- @skipVBlank:
- _irq: ;lmao
- rti
- _spin: ;void (void)
- bit PPUSTATUS ;did vblank happen? (bit 7=1)
- bpl _spin ;if not, go back
- rts
- ;turns fixed point number with 6 bits of fraction to int
- _toIntU: ;uint16_t (uint16_t)
- ldy #3
- bpl _toIntU_start ;always branch
- _toIntS: ;int16_t (int16_t)
- ldy #2
- _toIntU_start:
- stx swap
- bit swap
- bpl @posNumber
- dey
- @posNumber:
- lsr swap ;unrolled loop
- ror
- lsr swap
- ror
- lsr swap
- ror
- lsr swap
- ror
- lsr swap
- ror
- lsr swap
- ror
- dey
- bne @negNumber
- tay ;bit extension if negative
- lda #%11111100 ;^^
- ora swap ;^^
- sta swap ;^^
- tya ;^^
- @negNumber:
- ldx swap
- rts
- ;8-bit xorshift prng
- ;source: https://gist.github.com/bhickey/0de228c02cc60b5965582d2d946d8c38
- _random8: ;uint8_t (void)
- lda _randomNuml
- asl
- eor _randomNuml
- sta _randomNuml
- lsr
- eor _randomNuml
- sta _randomNuml
- asl
- asl
- eor _randomNuml
- sta _randomNuml
- rts
- ;16-bit xorshift prng
- ;(gonna be honest, this routine is a black box that i
- ;don't fully understand)
- ;source: https://codebase64.org/doku.php?id=base:16bit_xorshift_random_generator
- _random16: ;uint16_t (void)
- lda _randomNumh
- lsr
- lda _randomNuml
- ror
- eor _randomNumh
- sta _randomNumh ; high part of x ^= x << 7 done
- ror ; A has now x >> 9 and high bit comes from low byte
- eor _randomNumh
- sta _randomNuml ; x ^= x >> 9 and the low part of x ^= x << 7 done
- eor _randomNumh
- sta _randomNumh ; x ^= x << 8 done
- tax ;set up __fastcall__ return
- lda _randomNuml ;^^
- rts
- ;most of this yoinked from nesdev iirc
- ;this populates joypadState 1&2 with current controller state
- _getButtons: ;U_Reg16 (void)
- ;set up poll
- lda #$01 ;start refreshing controller(s) state
- sta JOYPAD1 ;^^
- sta _joypadState2 ;player2r=1 so carry bit after 8th button read is 1
- lsr ;stop refreshing controller(s) state (a=0 now)
- sta JOYPAD1 ;^^
- @readLoop:
- lda JOYPAD1 ;bit 0 from JOYPAD1 -> carry flag
- lsr ;^^
- rol _joypadState1 ;controller 1 read buffer <- carry flag
- lda JOYPAD2 ;bit 0 from JOYPAD2 -> carry flag
- lsr ;^^
- rol _joypadState2 ;controller 2 read buffer <- carry flag
- bcc @readLoop ;once player1r rotates 8 times, carry = 1
- ;lda _joypadState1 ;set up __fastcall__ return
- ;ldx _joypadState2 ;^^(low byte=player 1; high=player 2)
- rts
- _putString:
- rts
- _putPalColor: ;void (uint8_t index,uint8_t color)
- ;tbd overhaul palette buffer, fstatus, so all of that happens
- ;during vblank, and have separate routines
- _putPalBackground: ;void (const uint8_t*)
- bit PPUSTATUS
- ldy #>PPUPAL_BGUNI
- sty PPUADDR
- ldy #<PPUPAL_BGUNI
- sty PPUADDR
- ldy #0
- beq paletteCopy ;branch always
- _putPalSprite: ;void (const uint8_t*)
- bit PPUSTATUS
- ldy #>PPUPAL_SP0
- sty PPUADDR
- ldy #<PPUPAL_SP0
- sty PPUADDR
- ldy #0
- paletteCopy:
- sta r16al
- stx r16ah
- @loop:
- lda (r16al),y ;partially unrolled loop
- sta PPUDATA ;^^(loops 4 times instead of 16)
- iny
- lda (r16al),y
- sta PPUDATA
- iny
- lda (r16al),y
- sta PPUDATA
- iny
- lda (r16al),y
- sta PPUDATA
- iny
- cpy #16
- bne @loop
- rts
- ;fixed point sin/cos fastcall function (radians)
- ;takes unsigned 16-bit, returns signed 16 bit
- ;uses 6 bits of fraction
- ;2pi=402=110.010010
- ;cos(x)=sin(x-.5pi)=sin(x+1.5pi)
- _cos: ;int16_t (uint16_t)
- COSADD=95;(302-256)
- clc ;add 302, or ~1.5pi (46+256)
- adc #COSADD ;^^(a circle seems to deform at that actually,
- bcc _sin ;^^but seems to work after adding another .25pi.)
- inx ;^^increment for high byte
- _sin: ;int16_t (uint16_t)
- sta r16al ;dividend
- stx r16ah ;^^
- lda #<$0192 ;divisor ($192=402=146+256)
- sta r16bl ;^^
- lda #>$0192 ;^^
- sta r16bh ;^^
- ;division code yoinked from (bc i'm too dumb to make my own):
- ;https://codebase64.org/doku.php?id=base:16bit_division_16-bit_result
- ;r16a=dividend; r16b=divisor; r16c=remainder result=swap
- lda #0 ;preset remainder to 0
- sta r16cl
- sta r16ch
- ldx #16 ;repeat for each bit: ...
- @divloop:
- asl r16al ;dividend lb & hb*2, msb -> Carry
- rol r16ah
- rol r16cl ;remainder lb & hb * 2 + msb from carry
- rol r16ch
- lda r16cl
- sec
- sbc r16bl ;substract divisor to see if it fits in
- tay ;lb result -> Y, for we may need it later
- lda r16ch
- sbc r16bh
- bcc @skip ;if carry=0 then divisor didn't fit in yet
- sta r16ch ;else save substraction result as new remainder,
- sty r16cl
- inc swap ;and INCrement result cause divisor fit in 1 times
- @skip:
- dex
- bne @divloop
- ;;
- lda r16cl ;add location of lookup table to offset
- clc ;^^
- adc #<sin_table ;^^
- sta r16cl ;^^
- lda r16ch ;^^high byte
- adc #>sin_table ;^^
- sta r16ch ;^^
- ldx #0 ;init extended sign to 0
- lda (r16cl,x) ;
- bpl @no_sign_extension ;if low byte is negative, extend sign of high byte to $ff
- dex ;x=#%11111111 ;^^
- @no_sign_extension:
- rts
- .segment "RODATA"
- sin_table:;00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
- .byte $00,$01,$02,$03,$04,$05,$06,$07,$08,$09,$0a,$0b,$0c,$0d,$0e,$0f ;0000
- .byte $10,$11,$12,$13,$14,$15,$16,$17,$17,$18,$19,$1a,$1b,$1c,$1d,$1e ;0010
- .byte $1f,$20,$20,$21,$22,$23,$24,$25,$25,$26,$27,$28,$29,$29,$2a,$2b ;0020
- .byte $2c,$2c,$2d,$2e,$2e,$2f,$30,$30,$31,$32,$32,$33,$34,$34,$35,$35 ;0030
- .byte $36,$36,$37,$37,$38,$38,$39,$39,$3a,$3a,$3b,$3b,$3b,$3c,$3c,$3c ;0040
- .byte $3d,$3d,$3d,$3e,$3e,$3e,$3e,$3f,$3f,$3f,$3f,$3f,$3f,$40,$40,$40 ;0050
- .byte $40,$40,$40,$40,$40,$40,$40,$40,$40,$40,$40,$40,$40,$3f,$3f,$3f ;0060
- .byte $3f,$3f,$3f,$3e,$3e,$3e,$3e,$3d,$3d,$3d,$3c,$3c,$3c,$3b,$3b,$3b ;0070
- .byte $3a,$3a,$39,$39,$38,$38,$37,$37,$36,$36,$35,$35,$34,$34,$33,$32 ;0080
- .byte $32,$31,$31,$30,$2f,$2f,$2e,$2d,$2c,$2c,$2b,$2a,$29,$29,$28,$27 ;0090
- .byte $26,$25,$25,$24,$23,$22,$21,$20,$20,$1f,$1e,$1d,$1c,$1b,$1a,$19 ;00A0
- .byte $18,$17,$17,$16,$15,$14,$13,$12,$11,$10,$0f,$0e,$0d,$0c,$0b,$0a ;00B0
- .byte $09,$08,$07,$06,$05,$04,$03,$02,$01,$00,$ff,$fe,$fd,$fc,$fb,$fa ;00C0
- .byte $f9,$f8,$f7,$f6,$f5,$f4,$f3,$f2,$f1,$f0,$ef,$ee,$ed,$ec,$eb,$ea ;00D0
- .byte $ea,$e9,$e8,$e7,$e6,$e5,$e4,$e3,$e2,$e1,$e0,$e0,$df,$de,$dd,$dc ;00E0
- .byte $db,$db,$da,$d9,$d8,$d7,$d7,$d6,$d5,$d4,$d4,$d3,$d2,$d2,$d1,$d0 ;00F0
- .byte $d0,$cf,$ce,$ce,$cd,$cc,$cc,$cb,$cb,$ca,$ca,$c9,$c9,$c8,$c8,$c7 ;0100
- .byte $c7,$c6,$c6,$c5,$c5,$c5,$c4,$c4,$c4,$c3,$c3,$c3,$c2,$c2,$c2,$c2 ;0110
- .byte $c1,$c1,$c1,$c1,$c1,$c1,$c0,$c0,$c0,$c0,$c0,$c0,$c0,$c0,$c0,$c0 ;0120
- .byte $c0,$c0,$c0,$c0,$c0,$c0,$c1,$c1,$c1,$c1,$c1,$c1,$c2,$c2,$c2,$c2 ;0130
- .byte $c3,$c3,$c3,$c4,$c4,$c4,$c5,$c5,$c5,$c6,$c6,$c7,$c7,$c8,$c8,$c9 ;0140
- .byte $c9,$ca,$ca,$cb,$cb,$cc,$cc,$cd,$ce,$ce,$cf,$cf,$d0,$d1,$d1,$d2 ;0150
- .byte $d3,$d4,$d4,$d5,$d6,$d7,$d7,$d8,$d9,$da,$da,$db,$dc,$dd,$de,$df ;0160
- .byte $df,$e0,$e1,$e2,$e3,$e4,$e5,$e6,$e7,$e8,$e8,$e9,$ea,$eb,$ec,$ed ;0170
- .byte $ee,$ef,$f0,$f1,$f2,$f3,$f4,$f5,$f6,$f7,$f8,$f9,$fa,$fb,$fc,$fd ;0180
- .byte $fe,$ff ;0190
- .segment "VECTORS"
- .addr _vblank
- .addr _reset ;program counter initializes here iirc
- .addr _irq
- .segment "CHARS_A" ;left pattern table
- ;0: amogus
- .byte %00111111
- .byte %00111111
- .byte %11110111
- .byte %11110000
- .byte %11111111
- .byte %11111111
- .byte %00110011
- .byte %00110011 ;
- .byte %00000000
- .byte %00000000
- .byte %00001111
- .byte %00001111
- .byte %00000000
- .byte %00000000
- .byte %00000000
- .byte %00000000 ;
- ;1: paddle left
- .byte %01011101
- .byte %00101110
- .byte %01011101
- .byte %00101110
- .byte %01011101
- .byte %00101110
- .byte %01011101
- .byte %00101110 ;
- .byte %00000010
- .byte %00000001
- .byte %00000010
- .byte %00000001
- .byte %00000010
- .byte %00000001
- .byte %00000010
- .byte %00000001 ;
- ;2: paddle right
- .byte %00010111
- .byte %10001011
- .byte %00010111
- .byte %10001011
- .byte %00010111
- .byte %10001011
- .byte %00010111
- .byte %10001011 ;
- .byte %11111111
- .byte %01111111
- .byte %11111111
- .byte %01111111
- .byte %11111111
- .byte %01111111
- .byte %11111111
- .byte %01111111 ;
- ;stars (this could just be 1 pattern with palette swaps)
- ;0/3
- .byte %00000000
- .byte %00010000
- .byte %00101000
- .byte %01010100
- .byte %00101000
- .byte %00010000
- .byte %00000000
- .byte %00000000 ;
- .byte %00010000
- .byte %01010100
- .byte %00111000
- .byte %11101110
- .byte %00111000
- .byte %01010100
- .byte %00010000
- .byte %00000000 ;
- ;1/3
- .byte %00010000
- .byte %01000100
- .byte %00010000
- .byte %10101010
- .byte %00010000
- .byte %01000100
- .byte %00010000
- .byte %00000000 ;
- .byte %00010000
- .byte %01010100
- .byte %00101000
- .byte %11010110
- .byte %00101000
- .byte %01010100
- .byte %00010000
- .byte %00000000 ;
- ;2/3
- .byte %00000000
- .byte %00010000
- .byte %00101000
- .byte %01010100
- .byte %00101000
- .byte %00010000
- .byte %00000000
- .byte %00000000 ;
- .byte %00010000
- .byte %01000100
- .byte %00010000
- .byte %10111010
- .byte %00010000
- .byte %01000100
- .byte %00010000
- .byte %00000000 ;
- ;3/3
- .byte %00010000
- .byte %01000100
- .byte %00010000
- .byte %10101010
- .byte %00010000
- .byte %01000100
- .byte %00010000
- .byte %00000000 ;
- .byte %00000000
- .byte %00010000
- .byte %00111000
- .byte %01111100
- .byte %00111000
- .byte %00010000
- .byte %00000000
- .byte %00000000 ;
- .segment "CHARS_B" ;right pattern table
- .byte %10000001
- .byte %00000000
- .byte %00000000
- .byte %00000000
- .byte %00000000
- .byte %00000000
- .byte %00000000
- .byte %10000001 ;
- .byte %10000001
- .byte %00000000
- .byte %00000000
- .byte %00000000
- .byte %00000000
- .byte %00000000
- .byte %00000000
- .byte %10000001 ;
- .include "text8x8.inc"
- .segment "DATA"
Add Comment
Please, Sign In to add comment