├── test ├── .gitignore ├── nestress │ ├── Header_Mapper0 │ ├── Tanks.chr │ ├── NEStress.nes │ ├── Make_NEStress │ ├── TanksFadePal.ASM │ ├── FIREWAVE.ASM │ └── TanksPal.ASM ├── blarg.NES ├── nestest.nes ├── instr_misc.nes ├── mmc3 │ ├── 5-MMC3.nes │ ├── 1-clocking.nes │ ├── 2-details.nes │ ├── 6-MMC3_alt.nes │ ├── 3-A12_clocking.nes │ └── 4-scanline_timing.nes ├── apu │ ├── apu_test.nes │ ├── source │ │ ├── common │ │ │ ├── ascii.chr │ │ │ ├── devcart.bin │ │ │ ├── sync_apu.s │ │ │ ├── test_main_chans.s │ │ │ ├── shell.inc │ │ │ ├── apu_shell.inc │ │ │ ├── neshw.inc │ │ │ ├── sync_dmc.s │ │ │ ├── text_out.s │ │ │ ├── build_rom.s │ │ │ ├── crc.s │ │ │ ├── testing.s │ │ │ └── shell.s │ │ ├── 2-len_table.s │ │ ├── 8-dmc_rates.s │ │ ├── 3-irq_flag.s │ │ ├── 6-irq_flag_timing.s │ │ ├── 4-jitter.s │ │ ├── nes.cfg │ │ ├── 5-len_timing.s │ │ └── 1-len_ctr.s │ ├── rom_singles │ │ ├── 1-len_ctr.nes │ │ ├── 3-irq_flag.nes │ │ ├── 4-jitter.nes │ │ ├── 2-len_table.nes │ │ ├── 5-len_timing.nes │ │ ├── 7-dmc_basics.nes │ │ ├── 8-dmc_rates.nes │ │ └── 6-irq_flag_timing.nes │ └── apu_scale.s ├── instr │ ├── 13-rts.nes │ ├── 14-rti.nes │ ├── 15-brk.nes │ ├── 01-basics.nes │ ├── 02-implied.nes │ ├── 05-zp_xy.nes │ ├── 07-abs_xy.nes │ ├── 08-ind_x.nes │ ├── 09-ind_y.nes │ ├── 11-stack.nes │ ├── 12-jmp_jsr.nes │ ├── 16-special.nes │ ├── 03-immediate.nes │ ├── 04-zero_page.nes │ ├── 06-absolute.nes │ └── 10-branches.nes ├── ppu │ ├── ppu_vbl_nmi.nes │ ├── source │ │ ├── common │ │ │ ├── ascii.chr │ │ │ ├── bootloader.bin │ │ │ ├── shell.inc │ │ │ ├── neshw.inc │ │ │ ├── text_out.s │ │ │ ├── build_rom.s │ │ │ ├── crc.s │ │ │ ├── testing.s │ │ │ └── shell.s │ │ ├── 03-vbl_clear_time.s │ │ ├── 02-vbl_set_time.s │ │ ├── 07-nmi_on_timing.s │ │ ├── 08-nmi_off_timing.s │ │ ├── 05-nmi_timing.s │ │ ├── 06-suppression.s │ │ ├── 01-vbl_basics.s │ │ ├── 09-even_odd_frames.s │ │ ├── nes.cfg │ │ ├── 10-even_odd_timing.s │ │ └── 04-nmi_control.s │ └── rom_singles │ │ ├── 01-vbl_basics.nes │ │ ├── 02-vbl_set_time.nes │ │ ├── 04-nmi_control.nes │ │ ├── 05-nmi_timing.nes │ │ ├── 06-suppression.nes │ │ ├── 03-vbl_clear_time.nes │ │ ├── 07-nmi_on_timing.nes │ │ ├── 08-nmi_off_timing.nes │ │ ├── 09-even_odd_frames.nes │ │ └── 10-even_odd_timing.nes ├── ppu2 │ ├── palette_ram.nes │ ├── sprite_ram.nes │ ├── vram_access.nes │ ├── power_up_palette.nes │ ├── vbl_clear_time.nes │ ├── source │ │ ├── power_up_palette.asm │ │ ├── vbl_clear_time.asm │ │ ├── prefix_ppu.asm │ │ ├── palette_ram.asm │ │ └── sprite_ram.asm │ └── readme.txt ├── interrupt │ ├── 1-cli_latency.nes │ ├── 2-nmi_and_brk.nes │ ├── 3-nmi_and_irq.nes │ ├── 4-irq_and_dma.nes │ └── 5-branch_delays_irq.nes ├── sprite_hit_tests │ ├── 01.basics.nes │ ├── 04.flip.nes │ ├── 03.corners.nes │ ├── 02.alignment.nes │ ├── 05.left_clip.nes │ ├── 06.right_edge.nes │ ├── 11.edge_timing.nes │ ├── 07.screen_bottom.nes │ ├── 08.double_height.nes │ ├── 09.timing_basics.nes │ └── 10.timing_order.nes ├── sprite_overflow │ ├── 1.Basics.nes │ ├── 2.Details.nes │ ├── 3.Timing.nes │ ├── 4.Obscure.nes │ ├── 5.Emulator.nes │ └── source │ │ ├── runtime_rom.a │ │ ├── runtime_rom_common.a │ │ ├── console.a │ │ ├── 1.Basics.a │ │ ├── ppu_util.a │ │ ├── validation.a │ │ ├── debug.a │ │ ├── delays.a │ │ ├── ppu_sync.a │ │ ├── 2.Details.a │ │ ├── 5.Emulator.a │ │ ├── prefix_ppu.a │ │ └── prefix.a └── cpu_interrupts │ ├── cpu_interrupts.nes │ ├── source │ ├── common │ │ ├── ascii.chr │ │ ├── devcart.bin │ │ ├── sync_apu.s │ │ ├── shell.inc │ │ ├── neshw.inc │ │ ├── text_out.s │ │ ├── build_rom.s │ │ ├── crc.s │ │ ├── testing.s │ │ └── shell.s │ ├── nes.cfg │ ├── 4-irq_and_dma.s │ ├── 2-nmi_and_brk.s │ ├── 3-nmi_and_irq.s │ └── 5-branch_delays_irq.s │ └── rom_singles │ ├── 1-cli_latency.nes │ ├── 2-nmi_and_brk.nes │ ├── 3-nmi_and_irq.nes │ ├── 4-irq_and_dma.nes │ └── 5-branch_delays_irq.nes ├── .npmignore ├── .gitignore ├── .jshintignore ├── .jshintrc ├── palettes ├── fbx3.pal ├── ntsc.pal └── yuv.pal ├── bin ├── nesnes-server.js └── pal2js.js ├── Makefile ├── system ├── output │ ├── index.js │ ├── renderer │ │ ├── palette.js │ │ └── canvas2d.js │ ├── videooutput.js │ └── audiooutput.js ├── mappers │ ├── uxrom.js │ ├── axrom.js │ ├── nrom.js │ ├── index.js │ ├── mmc1.js │ └── mmc2.js ├── controllers │ ├── zapper.js │ ├── index.js │ └── standardcontroller.js ├── ppu │ └── memory.js ├── apu │ ├── noise.js │ ├── triangle.js │ ├── channel.js │ ├── dmc.js │ └── pulse.js ├── memory.js ├── utils │ └── bitmap.js └── input │ ├── keyboard.js │ ├── controllerhandler.js │ └── index.js ├── browser └── utils.js ├── utils.js ├── package.json ├── LICENSE ├── server └── index.def ├── config.json └── README.md /test/.gitignore: -------------------------------------------------------------------------------- 1 | nesnes.js 2 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | test 2 | palettes 3 | bin 4 | server -------------------------------------------------------------------------------- /test/nestress/Header_Mapper0: -------------------------------------------------------------------------------- 1 | NES -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.log 2 | *.swp 3 | node_modules 4 | dist 5 | -------------------------------------------------------------------------------- /.jshintignore: -------------------------------------------------------------------------------- 1 | .git 2 | node_modules 3 | test 4 | dist 5 | -------------------------------------------------------------------------------- /test/blarg.NES: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koenkivits/nesnes/HEAD/test/blarg.NES -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "esnext": true, 3 | "browserify": true, 4 | "unused": true 5 | } 6 | -------------------------------------------------------------------------------- /palettes/fbx3.pal: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koenkivits/nesnes/HEAD/palettes/fbx3.pal -------------------------------------------------------------------------------- /palettes/ntsc.pal: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koenkivits/nesnes/HEAD/palettes/ntsc.pal -------------------------------------------------------------------------------- /palettes/yuv.pal: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koenkivits/nesnes/HEAD/palettes/yuv.pal -------------------------------------------------------------------------------- /test/nestest.nes: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koenkivits/nesnes/HEAD/test/nestest.nes -------------------------------------------------------------------------------- /test/instr_misc.nes: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koenkivits/nesnes/HEAD/test/instr_misc.nes -------------------------------------------------------------------------------- /test/mmc3/5-MMC3.nes: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koenkivits/nesnes/HEAD/test/mmc3/5-MMC3.nes -------------------------------------------------------------------------------- /test/apu/apu_test.nes: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koenkivits/nesnes/HEAD/test/apu/apu_test.nes -------------------------------------------------------------------------------- /test/instr/13-rts.nes: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koenkivits/nesnes/HEAD/test/instr/13-rts.nes -------------------------------------------------------------------------------- /test/instr/14-rti.nes: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koenkivits/nesnes/HEAD/test/instr/14-rti.nes -------------------------------------------------------------------------------- /test/instr/15-brk.nes: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koenkivits/nesnes/HEAD/test/instr/15-brk.nes -------------------------------------------------------------------------------- /test/instr/01-basics.nes: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koenkivits/nesnes/HEAD/test/instr/01-basics.nes -------------------------------------------------------------------------------- /test/instr/02-implied.nes: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koenkivits/nesnes/HEAD/test/instr/02-implied.nes -------------------------------------------------------------------------------- /test/instr/05-zp_xy.nes: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koenkivits/nesnes/HEAD/test/instr/05-zp_xy.nes -------------------------------------------------------------------------------- /test/instr/07-abs_xy.nes: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koenkivits/nesnes/HEAD/test/instr/07-abs_xy.nes -------------------------------------------------------------------------------- /test/instr/08-ind_x.nes: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koenkivits/nesnes/HEAD/test/instr/08-ind_x.nes -------------------------------------------------------------------------------- /test/instr/09-ind_y.nes: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koenkivits/nesnes/HEAD/test/instr/09-ind_y.nes -------------------------------------------------------------------------------- /test/instr/11-stack.nes: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koenkivits/nesnes/HEAD/test/instr/11-stack.nes -------------------------------------------------------------------------------- /test/instr/12-jmp_jsr.nes: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koenkivits/nesnes/HEAD/test/instr/12-jmp_jsr.nes -------------------------------------------------------------------------------- /test/instr/16-special.nes: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koenkivits/nesnes/HEAD/test/instr/16-special.nes -------------------------------------------------------------------------------- /test/mmc3/1-clocking.nes: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koenkivits/nesnes/HEAD/test/mmc3/1-clocking.nes -------------------------------------------------------------------------------- /test/mmc3/2-details.nes: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koenkivits/nesnes/HEAD/test/mmc3/2-details.nes -------------------------------------------------------------------------------- /test/mmc3/6-MMC3_alt.nes: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koenkivits/nesnes/HEAD/test/mmc3/6-MMC3_alt.nes -------------------------------------------------------------------------------- /test/nestress/Tanks.chr: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koenkivits/nesnes/HEAD/test/nestress/Tanks.chr -------------------------------------------------------------------------------- /test/ppu/ppu_vbl_nmi.nes: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koenkivits/nesnes/HEAD/test/ppu/ppu_vbl_nmi.nes -------------------------------------------------------------------------------- /test/ppu2/palette_ram.nes: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koenkivits/nesnes/HEAD/test/ppu2/palette_ram.nes -------------------------------------------------------------------------------- /test/ppu2/sprite_ram.nes: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koenkivits/nesnes/HEAD/test/ppu2/sprite_ram.nes -------------------------------------------------------------------------------- /test/ppu2/vram_access.nes: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koenkivits/nesnes/HEAD/test/ppu2/vram_access.nes -------------------------------------------------------------------------------- /test/instr/03-immediate.nes: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koenkivits/nesnes/HEAD/test/instr/03-immediate.nes -------------------------------------------------------------------------------- /test/instr/04-zero_page.nes: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koenkivits/nesnes/HEAD/test/instr/04-zero_page.nes -------------------------------------------------------------------------------- /test/instr/06-absolute.nes: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koenkivits/nesnes/HEAD/test/instr/06-absolute.nes -------------------------------------------------------------------------------- /test/instr/10-branches.nes: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koenkivits/nesnes/HEAD/test/instr/10-branches.nes -------------------------------------------------------------------------------- /test/nestress/NEStress.nes: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koenkivits/nesnes/HEAD/test/nestress/NEStress.nes -------------------------------------------------------------------------------- /test/mmc3/3-A12_clocking.nes: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koenkivits/nesnes/HEAD/test/mmc3/3-A12_clocking.nes -------------------------------------------------------------------------------- /test/nestress/Make_NEStress: -------------------------------------------------------------------------------- 1 | DAsm NEStress.asm -f3 -v3 2 | Join Header_Mapper0 a.out Tanks.CHR AS NEStress.NES 3 | -------------------------------------------------------------------------------- /test/ppu2/power_up_palette.nes: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koenkivits/nesnes/HEAD/test/ppu2/power_up_palette.nes -------------------------------------------------------------------------------- /test/ppu2/vbl_clear_time.nes: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koenkivits/nesnes/HEAD/test/ppu2/vbl_clear_time.nes -------------------------------------------------------------------------------- /test/apu/source/common/ascii.chr: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koenkivits/nesnes/HEAD/test/apu/source/common/ascii.chr -------------------------------------------------------------------------------- /test/interrupt/1-cli_latency.nes: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koenkivits/nesnes/HEAD/test/interrupt/1-cli_latency.nes -------------------------------------------------------------------------------- /test/interrupt/2-nmi_and_brk.nes: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koenkivits/nesnes/HEAD/test/interrupt/2-nmi_and_brk.nes -------------------------------------------------------------------------------- /test/interrupt/3-nmi_and_irq.nes: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koenkivits/nesnes/HEAD/test/interrupt/3-nmi_and_irq.nes -------------------------------------------------------------------------------- /test/interrupt/4-irq_and_dma.nes: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koenkivits/nesnes/HEAD/test/interrupt/4-irq_and_dma.nes -------------------------------------------------------------------------------- /test/mmc3/4-scanline_timing.nes: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koenkivits/nesnes/HEAD/test/mmc3/4-scanline_timing.nes -------------------------------------------------------------------------------- /test/ppu/source/common/ascii.chr: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koenkivits/nesnes/HEAD/test/ppu/source/common/ascii.chr -------------------------------------------------------------------------------- /test/apu/rom_singles/1-len_ctr.nes: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koenkivits/nesnes/HEAD/test/apu/rom_singles/1-len_ctr.nes -------------------------------------------------------------------------------- /test/apu/rom_singles/3-irq_flag.nes: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koenkivits/nesnes/HEAD/test/apu/rom_singles/3-irq_flag.nes -------------------------------------------------------------------------------- /test/apu/rom_singles/4-jitter.nes: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koenkivits/nesnes/HEAD/test/apu/rom_singles/4-jitter.nes -------------------------------------------------------------------------------- /test/apu/source/common/devcart.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koenkivits/nesnes/HEAD/test/apu/source/common/devcart.bin -------------------------------------------------------------------------------- /test/sprite_hit_tests/01.basics.nes: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koenkivits/nesnes/HEAD/test/sprite_hit_tests/01.basics.nes -------------------------------------------------------------------------------- /test/sprite_hit_tests/04.flip.nes: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koenkivits/nesnes/HEAD/test/sprite_hit_tests/04.flip.nes -------------------------------------------------------------------------------- /test/sprite_overflow/1.Basics.nes: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koenkivits/nesnes/HEAD/test/sprite_overflow/1.Basics.nes -------------------------------------------------------------------------------- /test/sprite_overflow/2.Details.nes: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koenkivits/nesnes/HEAD/test/sprite_overflow/2.Details.nes -------------------------------------------------------------------------------- /test/sprite_overflow/3.Timing.nes: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koenkivits/nesnes/HEAD/test/sprite_overflow/3.Timing.nes -------------------------------------------------------------------------------- /test/sprite_overflow/4.Obscure.nes: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koenkivits/nesnes/HEAD/test/sprite_overflow/4.Obscure.nes -------------------------------------------------------------------------------- /test/sprite_overflow/5.Emulator.nes: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koenkivits/nesnes/HEAD/test/sprite_overflow/5.Emulator.nes -------------------------------------------------------------------------------- /test/apu/rom_singles/2-len_table.nes: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koenkivits/nesnes/HEAD/test/apu/rom_singles/2-len_table.nes -------------------------------------------------------------------------------- /test/apu/rom_singles/5-len_timing.nes: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koenkivits/nesnes/HEAD/test/apu/rom_singles/5-len_timing.nes -------------------------------------------------------------------------------- /test/apu/rom_singles/7-dmc_basics.nes: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koenkivits/nesnes/HEAD/test/apu/rom_singles/7-dmc_basics.nes -------------------------------------------------------------------------------- /test/apu/rom_singles/8-dmc_rates.nes: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koenkivits/nesnes/HEAD/test/apu/rom_singles/8-dmc_rates.nes -------------------------------------------------------------------------------- /test/ppu/source/common/bootloader.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koenkivits/nesnes/HEAD/test/ppu/source/common/bootloader.bin -------------------------------------------------------------------------------- /test/sprite_hit_tests/03.corners.nes: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koenkivits/nesnes/HEAD/test/sprite_hit_tests/03.corners.nes -------------------------------------------------------------------------------- /test/cpu_interrupts/cpu_interrupts.nes: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koenkivits/nesnes/HEAD/test/cpu_interrupts/cpu_interrupts.nes -------------------------------------------------------------------------------- /test/interrupt/5-branch_delays_irq.nes: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koenkivits/nesnes/HEAD/test/interrupt/5-branch_delays_irq.nes -------------------------------------------------------------------------------- /test/ppu/rom_singles/01-vbl_basics.nes: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koenkivits/nesnes/HEAD/test/ppu/rom_singles/01-vbl_basics.nes -------------------------------------------------------------------------------- /test/ppu/rom_singles/02-vbl_set_time.nes: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koenkivits/nesnes/HEAD/test/ppu/rom_singles/02-vbl_set_time.nes -------------------------------------------------------------------------------- /test/ppu/rom_singles/04-nmi_control.nes: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koenkivits/nesnes/HEAD/test/ppu/rom_singles/04-nmi_control.nes -------------------------------------------------------------------------------- /test/ppu/rom_singles/05-nmi_timing.nes: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koenkivits/nesnes/HEAD/test/ppu/rom_singles/05-nmi_timing.nes -------------------------------------------------------------------------------- /test/ppu/rom_singles/06-suppression.nes: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koenkivits/nesnes/HEAD/test/ppu/rom_singles/06-suppression.nes -------------------------------------------------------------------------------- /test/sprite_hit_tests/02.alignment.nes: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koenkivits/nesnes/HEAD/test/sprite_hit_tests/02.alignment.nes -------------------------------------------------------------------------------- /test/sprite_hit_tests/05.left_clip.nes: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koenkivits/nesnes/HEAD/test/sprite_hit_tests/05.left_clip.nes -------------------------------------------------------------------------------- /test/sprite_hit_tests/06.right_edge.nes: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koenkivits/nesnes/HEAD/test/sprite_hit_tests/06.right_edge.nes -------------------------------------------------------------------------------- /test/sprite_hit_tests/11.edge_timing.nes: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koenkivits/nesnes/HEAD/test/sprite_hit_tests/11.edge_timing.nes -------------------------------------------------------------------------------- /test/apu/rom_singles/6-irq_flag_timing.nes: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koenkivits/nesnes/HEAD/test/apu/rom_singles/6-irq_flag_timing.nes -------------------------------------------------------------------------------- /test/ppu/rom_singles/03-vbl_clear_time.nes: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koenkivits/nesnes/HEAD/test/ppu/rom_singles/03-vbl_clear_time.nes -------------------------------------------------------------------------------- /test/ppu/rom_singles/07-nmi_on_timing.nes: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koenkivits/nesnes/HEAD/test/ppu/rom_singles/07-nmi_on_timing.nes -------------------------------------------------------------------------------- /test/ppu/rom_singles/08-nmi_off_timing.nes: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koenkivits/nesnes/HEAD/test/ppu/rom_singles/08-nmi_off_timing.nes -------------------------------------------------------------------------------- /test/sprite_hit_tests/07.screen_bottom.nes: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koenkivits/nesnes/HEAD/test/sprite_hit_tests/07.screen_bottom.nes -------------------------------------------------------------------------------- /test/sprite_hit_tests/08.double_height.nes: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koenkivits/nesnes/HEAD/test/sprite_hit_tests/08.double_height.nes -------------------------------------------------------------------------------- /test/sprite_hit_tests/09.timing_basics.nes: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koenkivits/nesnes/HEAD/test/sprite_hit_tests/09.timing_basics.nes -------------------------------------------------------------------------------- /test/sprite_hit_tests/10.timing_order.nes: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koenkivits/nesnes/HEAD/test/sprite_hit_tests/10.timing_order.nes -------------------------------------------------------------------------------- /test/cpu_interrupts/source/common/ascii.chr: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koenkivits/nesnes/HEAD/test/cpu_interrupts/source/common/ascii.chr -------------------------------------------------------------------------------- /test/cpu_interrupts/source/common/devcart.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koenkivits/nesnes/HEAD/test/cpu_interrupts/source/common/devcart.bin -------------------------------------------------------------------------------- /test/ppu/rom_singles/09-even_odd_frames.nes: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koenkivits/nesnes/HEAD/test/ppu/rom_singles/09-even_odd_frames.nes -------------------------------------------------------------------------------- /test/ppu/rom_singles/10-even_odd_timing.nes: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koenkivits/nesnes/HEAD/test/ppu/rom_singles/10-even_odd_timing.nes -------------------------------------------------------------------------------- /test/cpu_interrupts/rom_singles/1-cli_latency.nes: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koenkivits/nesnes/HEAD/test/cpu_interrupts/rom_singles/1-cli_latency.nes -------------------------------------------------------------------------------- /test/cpu_interrupts/rom_singles/2-nmi_and_brk.nes: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koenkivits/nesnes/HEAD/test/cpu_interrupts/rom_singles/2-nmi_and_brk.nes -------------------------------------------------------------------------------- /test/cpu_interrupts/rom_singles/3-nmi_and_irq.nes: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koenkivits/nesnes/HEAD/test/cpu_interrupts/rom_singles/3-nmi_and_irq.nes -------------------------------------------------------------------------------- /test/cpu_interrupts/rom_singles/4-irq_and_dma.nes: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koenkivits/nesnes/HEAD/test/cpu_interrupts/rom_singles/4-irq_and_dma.nes -------------------------------------------------------------------------------- /test/cpu_interrupts/rom_singles/5-branch_delays_irq.nes: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koenkivits/nesnes/HEAD/test/cpu_interrupts/rom_singles/5-branch_delays_irq.nes -------------------------------------------------------------------------------- /bin/nesnes-server.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | var Server = require( "../server" ); 4 | 5 | var argv = process.argv.slice( 2 ); 6 | var server = new Server( argv[ 0 ], argv[ 1 ], argv[ 2 ] ); 7 | server.run(); -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | all: nesnes 2 | 3 | nesnes: lint dist 4 | node_modules/.bin/browserify index.js -s NesNes -o dist/nesnes.js 5 | 6 | lint: 7 | node_modules/.bin/jshint . 8 | 9 | dist: 10 | mkdir -p dist 11 | 12 | .PHONY: test nesnes -------------------------------------------------------------------------------- /system/output/index.js: -------------------------------------------------------------------------------- 1 | var AudioOutput = require("./audiooutput"); 2 | var VideoOutput = require("./videooutput"); 3 | 4 | function Output() { 5 | this.audio = new AudioOutput(); 6 | this.video = new VideoOutput(); 7 | } 8 | 9 | module.exports = Output; -------------------------------------------------------------------------------- /test/sprite_overflow/source/runtime_rom.a: -------------------------------------------------------------------------------- 1 | ; Build as standalone NES ROM using console for output 2 | 3 | .include "runtime_rom_common.a" 4 | 5 | patch_reset_then_wait: 6 | exit: jmp exit 7 | 8 | .default reset = main 9 | 10 | .org $fffa 11 | .dw nmi 12 | .dw reset 13 | .dw irq 14 | -------------------------------------------------------------------------------- /browser/utils.js: -------------------------------------------------------------------------------- 1 | exports.readFile = function( filename, callback ) { 2 | var xhr = new XMLHttpRequest(); 3 | xhr.open( "GET", filename ); 4 | xhr.responseType = "arraybuffer"; 5 | 6 | xhr.onload = function() { 7 | callback( xhr.response ); 8 | }; 9 | 10 | xhr.onerror = function( e ) { 11 | throw e; 12 | }; 13 | 14 | xhr.send( null ); 15 | }; -------------------------------------------------------------------------------- /system/mappers/uxrom.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | init: function() { 3 | this.prgBank = 0; 4 | this.loadPRGBank( 0x8000, this.prgBank, 0x4000 ); 5 | this.loadPRGBank( 0xc000, this.prgBanks - 1, 0x4000 ); 6 | }, 7 | 8 | writeRegister: function( address, value ) { 9 | this.prgBank = value & 0xf; // TODO, difference between UNROM and UOROM? 10 | this.loadPRGBank( 0x8000, this.prgBank, 0x4000 ); 11 | } 12 | }; -------------------------------------------------------------------------------- /system/mappers/axrom.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | init: function() { 3 | this.axRomBanks = this.prgBanks >> 1; 4 | this.setPrgBank( 0 ); 5 | }, 6 | 7 | setPrgBank: function( bank ) { 8 | this.prgBank = bank; 9 | this.loadPRGBank( 0x8000, bank, 0x8000 ); 10 | }, 11 | 12 | writeRegister: function( address, value ) { 13 | this.setPrgBank( value & 7 ); 14 | 15 | this.mirroring = ( value & 0x10 ) ? 4 : 3; 16 | } 17 | }; -------------------------------------------------------------------------------- /test/nestress/TanksFadePal.ASM: -------------------------------------------------------------------------------- 1 | ; tanksfade.pal 2 | 3 | DC.B $2d, $1d, $01, $02, $05, $06, $1d, $06, $09, $1d, $09, $0c, $1d, $0d, $0d, $0d 4 | DC.B $00, $01, $02, $03, $04, $05, $06, $07, $08, $09, $0a, $0b, $0c, $2e, $0e, $0e 5 | DC.B $3d, $11, $12, $13, $14, $15, $16, $17, $18, $19, $1a, $1b, $1c, $1d, $1e, $1e 6 | DC.B $20, $21, $22, $23, $24, $25, $26, $27, $28, $29, $2a, $2b, $2c, $10, $2e, $2e 7 | -------------------------------------------------------------------------------- /utils.js: -------------------------------------------------------------------------------- 1 | var fs = require("fs"); 2 | 3 | /** 4 | * Wrapper around fs.readFile to make it easily replaceble by Browserify and to 5 | * handle converting to an ArrayBuffer in a single place. 6 | */ 7 | exports.readFile = function( filename, callback ) { 8 | fs.readFile( filename, function( err, data ) { 9 | if ( err ) { 10 | throw err; 11 | } 12 | 13 | var buffer = new Uint8Array( data ).buffer; 14 | callback( buffer ); 15 | }); 16 | }; -------------------------------------------------------------------------------- /bin/pal2js.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Converts a .pal palette file to a NodeJS module. 3 | */ 4 | 5 | var fs = require("fs"); 6 | 7 | var filename = process.argv[2]; 8 | 9 | if ( !filename ) { 10 | throw new Error("Supply .pal file!"); 11 | } 12 | 13 | var buffer = fs.readFileSync(filename), 14 | glue = ""; 15 | 16 | write("exports.data = new Uint8Array(["); 17 | for ( var i = 0; i < buffer.length; i++ ) { 18 | write( glue + buffer[i] ); 19 | glue = ","; 20 | } 21 | write("]);"); 22 | 23 | function write( str ) { 24 | process.stdout.write( str ); 25 | } -------------------------------------------------------------------------------- /test/apu/source/common/sync_apu.s: -------------------------------------------------------------------------------- 1 | ; Synchronizes APU divide-by-two so that an STA $4017 2 | ; immediately after the JSR (or some even number of 3 | ; clocks after) will start the frame counter without 4 | ; an extra clock delay. 5 | ; Preserved: A, X, Y 6 | ; Time: 16.7 msec 7 | sync_apu: 8 | pha 9 | 10 | ; Clear IRQ flag 11 | sei 12 | lda #$40 13 | sta SNDMODE 14 | 15 | ; Mode 0, frame IRQ enabled 16 | lda #$00 17 | sta SNDMODE 18 | delay 29825 19 | 20 | ; Delay extra clock if odd alignment 21 | lda #$40 22 | bit SNDCHN 23 | bne :+ 24 | : 25 | ; Clear IRQ flag 26 | sta SNDMODE 27 | pla 28 | rts 29 | -------------------------------------------------------------------------------- /test/cpu_interrupts/source/common/sync_apu.s: -------------------------------------------------------------------------------- 1 | ; Synchronizes APU divide-by-two so that an STA $4017 2 | ; immediately after the JSR (or some even number of 3 | ; clocks after) will start the frame counter without 4 | ; an extra clock delay. 5 | ; Preserved: A, X, Y 6 | ; Time: 16.7 msec 7 | sync_apu: 8 | pha 9 | 10 | ; Clear IRQ flag 11 | sei 12 | lda #$40 13 | sta SNDMODE 14 | 15 | ; Mode 0, frame IRQ enabled 16 | lda #$00 17 | sta SNDMODE 18 | delay 29825 19 | 20 | ; Delay extra clock if odd alignment 21 | lda #$40 22 | bit SNDCHN 23 | bne :+ 24 | : 25 | ; Clear IRQ flag 26 | sta SNDMODE 27 | pla 28 | rts 29 | -------------------------------------------------------------------------------- /test/apu/source/common/test_main_chans.s: -------------------------------------------------------------------------------- 1 | zp_byte chan_idx 2 | zp_byte chan_off 3 | zp_byte chan_bit 4 | 5 | ; Calls routine for the four main channels 6 | .macro test_main_chans routine 7 | ldy #0 ; channel index: 0-3 8 | : sty chan_idx 9 | 10 | tya 11 | asl a 12 | asl a 13 | tax 14 | stx chan_off 15 | 16 | setb {$4000,x},$11 ; near-silent 17 | lda chan_bits,y 18 | sta chan_bit 19 | sta SNDCHN 20 | 21 | ; A = chan_bit 22 | ; X = chan_off 23 | ; Y = chan_idx 24 | jsr routine 25 | 26 | ldy chan_idx 27 | iny 28 | cpy #4 29 | bne :- 30 | .endmacro 31 | 32 | .align 4 33 | chan_bits: 34 | .byte $01,$02,$04,$08 35 | -------------------------------------------------------------------------------- /test/apu/source/common/shell.inc: -------------------------------------------------------------------------------- 1 | ; Included at beginning of program 2 | 3 | .ifdef CUSTOM_PREFIX 4 | .include "custom_prefix.s" 5 | .endif 6 | 7 | ; Sub-test in a multi-test ROM 8 | .ifdef BUILD_MULTI 9 | .include "build_multi.s" 10 | .else 11 | 12 | ; NSF music file 13 | .ifdef BUILD_NSF 14 | .include "build_nsf.s" 15 | .endif 16 | 17 | ; Devcart 18 | .ifdef BUILD_DEVCART 19 | .include "build_devcart.s" 20 | .endif 21 | 22 | ; NES internal RAM 23 | .ifdef BUILD_NOCART 24 | .include "build_nocart.s" 25 | .endif 26 | 27 | ; NES ROM (default) 28 | .ifndef SHELL_INCLUDED 29 | .include "build_rom.s" 30 | .endif 31 | 32 | .endif ; .ifdef BUILD_MULTI 33 | -------------------------------------------------------------------------------- /test/ppu/source/common/shell.inc: -------------------------------------------------------------------------------- 1 | ; Included at beginning of program 2 | 3 | .ifdef CUSTOM_PREFIX 4 | .include "custom_prefix.s" 5 | .endif 6 | 7 | ; Sub-test in a multi-test ROM 8 | .ifdef BUILD_MULTI 9 | .include "build_multi.s" 10 | .else 11 | 12 | ; NSF music file 13 | .ifdef BUILD_NSF 14 | .include "build_nsf.s" 15 | .endif 16 | 17 | ; Devcart 18 | .ifdef BUILD_DEVCART 19 | .include "build_devcart.s" 20 | .endif 21 | 22 | ; NES internal RAM 23 | .ifdef BUILD_NOCART 24 | .include "build_nocart.s" 25 | .endif 26 | 27 | ; NES ROM (default) 28 | .ifndef SHELL_INCLUDED 29 | .include "build_rom.s" 30 | .endif 31 | 32 | .endif ; .ifdef BUILD_MULTI 33 | -------------------------------------------------------------------------------- /system/mappers/nrom.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | init: function() { 3 | var nrom128 = ( this.prgBanks === 1 ); 4 | 5 | if ( nrom128 ) { 6 | this.loadPRGBank( 0x8000, 0, 0x4000 ); 7 | this.loadPRGBank( 0xc000, 0, 0x4000 ); 8 | } else { 9 | this.loadPRGBank( 0x8000, 0, 0x8000 ); 10 | } 11 | }, 12 | 13 | readCHR: function( address ) { 14 | return this.chrData[ address ]; 15 | }, 16 | 17 | writeCHR: function( address, value ) { 18 | if ( !this.chrBanks ) { 19 | // TODO, probably not doing this right for all ROMs (eg, ROMs that have both CHR ROM *and* CHR RAM) 20 | this.chrData[ address ] = value; 21 | } 22 | 23 | return value; 24 | } 25 | }; -------------------------------------------------------------------------------- /system/output/renderer/palette.js: -------------------------------------------------------------------------------- 1 | exports.data = new Uint8Array([102,102,102,0,42,136,20,18,167,59,0,164,92,0,126,110,0,64,108,7,0,86,29,0,51,53,0,12,72,0,0,82,0,0,79,8,0,64,77,0,0,0,0,0,0,0,0,0,173,173,173,21,95,217,66,64,255,117,39,254,160,26,204,183,30,123,181,49,32,153,78,0,107,109,0,56,135,0,13,147,0,0,143,50,0,124,141,0,0,0,0,0,0,0,0,0,255,255,255,100,176,255,146,144,255,198,118,255,242,106,255,255,110,204,255,129,112,234,158,34,188,190,0,136,216,0,92,228,48,69,224,130,72,205,222,79,79,79,0,0,0,0,0,0,255,255,255,192,223,255,211,210,255,232,200,255,250,194,255,255,196,234,255,204,197,247,216,165,228,229,148,207,239,150,189,244,171,179,243,204,181,235,242,184,184,184,0,0,0,0,0,0]); -------------------------------------------------------------------------------- /test/cpu_interrupts/source/common/shell.inc: -------------------------------------------------------------------------------- 1 | ; Included at beginning of program 2 | 3 | .ifdef CUSTOM_PREFIX 4 | .include "custom_prefix.s" 5 | .endif 6 | 7 | ; Sub-test in a multi-test ROM 8 | .ifdef BUILD_MULTI 9 | .include "build_multi.s" 10 | .else 11 | 12 | ; NSF music file 13 | .ifdef BUILD_NSF 14 | .include "build_nsf.s" 15 | .endif 16 | 17 | ; Devcart 18 | .ifdef BUILD_DEVCART 19 | .include "build_devcart.s" 20 | .endif 21 | 22 | ; NES internal RAM 23 | .ifdef BUILD_NOCART 24 | .include "build_nocart.s" 25 | .endif 26 | 27 | ; NES ROM (default) 28 | .ifndef SHELL_INCLUDED 29 | .include "build_rom.s" 30 | .endif 31 | 32 | .endif ; .ifdef BUILD_MULTI 33 | -------------------------------------------------------------------------------- /test/ppu/source/03-vbl_clear_time.s: -------------------------------------------------------------------------------- 1 | ; Tests time VBL flag is cleared. 2 | ; 3 | ; Reads $2002 and prints VBL flag. 4 | ; Test is run one PPU clock later each line, 5 | ; around the time the flag is cleared. 6 | ; 7 | ; 00 V 8 | ; 01 V 9 | ; 02 V 10 | ; 03 V 11 | ; 04 V 12 | ; 05 V 13 | ; 06 - 14 | ; 07 - 15 | ; 08 - 16 | 17 | .include "shell.inc" 18 | .include "sync_vbl.s" 19 | 20 | main: jsr console_hide 21 | loop_n_times test,9 22 | check_crc $E8ADB5BB 23 | jmp tests_passed 24 | 25 | test: jsr print_a 26 | jsr disable_rendering 27 | jsr sync_vbl_delay 28 | delay 29763 29 | delay 2273 30 | lda $2002 31 | print_cc bmi,'V','-' 32 | jsr print_newline 33 | rts 34 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "nesnes", 3 | "version": "0.3.1", 4 | "description": "New EcmaScript NES emulator", 5 | "main": "main.js", 6 | "keywords": [ 7 | "nesnes", 8 | "nes", 9 | "emulator" 10 | ], 11 | "repository": { 12 | "type": "git", 13 | "url": "https://github.com/koenkivits/nesnes.git" 14 | }, 15 | "scripts": { 16 | "prepublish": "make" 17 | }, 18 | "browser": { 19 | "./utils.js": "./browser/utils.js" 20 | }, 21 | "author": "Koen Kivits (http://koen.kivits.com/)", 22 | "license": "MIT", 23 | "devDependencies": { 24 | "browserify": "^10.2.4", 25 | "jshint": "^2.8.0", 26 | "dot": "^1.0.3" 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /test/apu/source/2-len_table.s: -------------------------------------------------------------------------------- 1 | ; Verifies all length table entries 2 | 3 | .include "apu_shell.inc" 4 | 5 | main: test_main_chans test 6 | jmp tests_passed 7 | 8 | test: loop_n_times test_entry,32 9 | rts 10 | 11 | test_entry: 12 | tay 13 | setb SNDMODE,0 14 | 15 | ; Load 16 | tya 17 | asl a 18 | asl a 19 | asl a 20 | ldx chan_off 21 | sta $4003,x 22 | 23 | ; Verify 24 | lda table,y 25 | tax 26 | dex 27 | : setb SNDMODE,$C0 ; clock length counter 28 | dex 29 | bne :- 30 | jsr len_should_be_1 31 | rts 32 | 33 | table: .byte 10, 254, 20, 2, 40, 4, 80, 6 34 | .byte 160, 8, 60, 10, 14, 12, 26, 14 35 | .byte 12, 16, 24, 18, 48, 20, 96, 22 36 | .byte 192, 24, 72, 26, 16, 28, 32, 30 37 | -------------------------------------------------------------------------------- /test/apu/source/common/apu_shell.inc: -------------------------------------------------------------------------------- 1 | .include "shell.inc" 2 | .include "sync_apu.s" 3 | .include "test_main_chans.s" 4 | 5 | len_2 = $18 ; value that loads length counter with 2 6 | len_4 = $28 ; value that loads length counter with 4 7 | len_6 = $38 ; value that loads length counter with 6 8 | 9 | halt = $B0 10 | unhalt = $10 11 | 12 | should_be_playing: 13 | lda chan_bit 14 | and SNDCHN 15 | jeq chan_failed 16 | rts 17 | 18 | len_should_be_1: 19 | jsr should_be_playing 20 | setb SNDMODE,$C0 ; clock length 21 | should_be_silent: 22 | lda chan_bit 23 | and SNDCHN 24 | jne chan_failed 25 | rts 26 | 27 | chan_failed: 28 | print_str "Channel: " 29 | lda chan_idx 30 | jsr print_dec 31 | jsr print_newline 32 | jmp test_failed 33 | -------------------------------------------------------------------------------- /test/sprite_overflow/source/runtime_rom_common.a: -------------------------------------------------------------------------------- 1 | 2 | .include "delays.a" 3 | .include "console.a" 4 | .include "debug.a" 5 | .include "ppu_util.a" 6 | 7 | console_ready = $7f1 8 | 9 | debug_char: 10 | pha 11 | lda #$a5 12 | cmp console_ready 13 | beq + 14 | sta console_ready 15 | txa 16 | pha 17 | tya 18 | pha 19 | jsr init_console 20 | pla 21 | tay 22 | pla 23 | tax 24 | : pla 25 | jmp print_char 26 | 27 | debug_newline: 28 | jsr console_newline 29 | jmp console_newline 30 | 31 | debug_char_no_wait: 32 | jmp print_char_no_wait 33 | 34 | init_runtime: 35 | clear_console_ready: 36 | lda #0 37 | sta console_ready 38 | rts 39 | 40 | forever: 41 | sei ; disable interrupts 42 | lda #0 43 | sta $2000 44 | jsr clear_console_ready 45 | jmp exit 46 | -------------------------------------------------------------------------------- /system/mappers/index.js: -------------------------------------------------------------------------------- 1 | var NROM = require("./nrom"), 2 | MMC1 = require("./mmc1"), 3 | MMC2 = require("./mmc2"), 4 | MMC3 = require("./mmc3"), 5 | UxROM = require("./uxrom"), 6 | AxROM = require("./axrom"); 7 | 8 | var mapperList = {}; 9 | 10 | mapperList[ 0 ] = NROM; 11 | mapperList[ 1 ] = MMC1; 12 | mapperList[ 2 ] = UxROM; 13 | mapperList[ 4 ] = MMC3; 14 | mapperList[ 7 ] = AxROM; 15 | mapperList[ 9 ] = MMC2; 16 | 17 | exports.init = function( cartridge ) { 18 | var mapper, method, 19 | mapperID = cartridge.mapper; 20 | 21 | if ( !( mapperID in mapperList ) ) { 22 | throw new Error( "Unknown mapper " + mapperID ); 23 | } 24 | 25 | mapper = mapperList[ mapperID ]; 26 | for ( method in mapper ) { 27 | cartridge[ method ] = mapper[ method ]; 28 | } 29 | 30 | cartridge.init(); 31 | }; -------------------------------------------------------------------------------- /test/ppu/source/02-vbl_set_time.s: -------------------------------------------------------------------------------- 1 | ; Verifies time VBL flag is set. 2 | ; 3 | ; Reads $2002 twice and prints VBL flags from 4 | ; them. Test is run one PPU clock later each time, 5 | ; around the time the flag is set. 6 | ; 7 | ; T+ 1 2 8 | ; 00 - V 9 | ; 01 - V 10 | ; 02 - V 11 | ; 03 - V ; after some resets this is - - 12 | ; 04 - - ; flag setting is suppressed 13 | ; 05 V - 14 | ; 06 V - 15 | ; 07 V - 16 | ; 08 V - 17 | 18 | .include "shell.inc" 19 | .include "sync_vbl.s" 20 | 21 | main: jsr console_hide 22 | print_str "T+ 1 2",newline 23 | loop_n_times test,9 24 | check_crc $1C3E0067 25 | jmp tests_passed 26 | 27 | test: jsr print_a 28 | jsr disable_rendering 29 | jsr sync_vbl_delay 30 | delay 29763 31 | ldx $2002 32 | ldy $2002 33 | txa 34 | print_cc bmi,'V','-' 35 | jsr print_space 36 | tya 37 | print_cc bmi,'V','-' 38 | jsr print_newline 39 | rts 40 | -------------------------------------------------------------------------------- /test/ppu/source/07-nmi_on_timing.s: -------------------------------------------------------------------------------- 1 | ; Tests NMI occurrence when enabled near time 2 | ; VBL flag is cleared. 3 | ; 4 | ; Enables NMI one PPU clock later on each line. 5 | ; Prints whether NMI occurred. 6 | ; 7 | ; 00 N 8 | ; 01 N 9 | ; 02 N 10 | ; 03 N 11 | ; 04 N 12 | ; 05 - 13 | ; 06 - 14 | ; 07 - 15 | ; 08 - 16 | 17 | CUSTOM_NMI=1 18 | .include "shell.inc" 19 | .include "sync_vbl.s" 20 | 21 | zp_byte nmi_count 22 | 23 | nmi: inc nmi_count 24 | rti 25 | 26 | main: jsr console_hide 27 | loop_n_times test,9 28 | check_crc $91410411 29 | jmp tests_passed 30 | 31 | test: jsr print_a 32 | jsr disable_rendering 33 | jsr sync_vbl_delay 34 | delay 29742+2287 35 | 36 | lda #0 37 | sta 2 | 3 | 4 | NESNES test server 5 | 6 | 15 | 16 | 17 |

NESNES test server

18 | 19 | 20 | 32 | 33 | 51 | -------------------------------------------------------------------------------- /test/ppu/source/01-vbl_basics.s: -------------------------------------------------------------------------------- 1 | ; Tests basic VBL operation and VBL period. 2 | 3 | .include "shell.inc" 4 | 5 | main: set_test 2,"VBL period is way off" 6 | jsr wait_vbl 7 | delay 30111 8 | lda $2002 9 | jpl test_failed 10 | 11 | set_test 3,"Reading VBL flag should clear it" 12 | lda $2002 13 | jmi test_failed 14 | 15 | set_test 4,"Writing $2002 shouldn't affect VBL flag" 16 | jsr wait_vbl 17 | delay 30111 18 | sta $2002 19 | lda $2002 20 | jpl test_failed 21 | 22 | set_test 5,"$2002 should be mirrored at $200A" 23 | jsr wait_vbl 24 | delay 30111 25 | lda $200A 26 | jpl test_failed 27 | lda $200A 28 | jmi test_failed 29 | 30 | set_test 6,"$2002 should be mirrored every 8 bytes up to $2FFA" 31 | jsr wait_vbl 32 | delay 30111 33 | lda $2FFA 34 | jpl test_failed 35 | lda $2FFA 36 | jmi test_failed 37 | 38 | delay_msec 1000 39 | 40 | lda #0 ; BG off 41 | sta $2001 42 | 43 | ; Delay 60 frames after VBL, then read VBL flag 44 | jsr wait_vbl 45 | delay 29780*60+60/3 46 | ldx $2002 47 | delay 5 48 | lda $2002 49 | 50 | set_test 7,"VBL period is too short with BG off" 51 | cpx #0 52 | jmi test_failed 53 | 54 | set_test 8,"VBL period is too long with BG off" 55 | cmp #0 56 | jpl test_failed 57 | 58 | jmp tests_passed 59 | -------------------------------------------------------------------------------- /config.json: -------------------------------------------------------------------------------- 1 | { 2 | "input": [ 3 | { 4 | "type": "standard", 5 | "controls": [ 6 | { 7 | "type": "keyboard", 8 | "config": { 9 | "z": "a", 10 | "x": "b", 11 | "shift": "select", 12 | "return": "start", 13 | "up": "up", 14 | "down": "down", 15 | "left": "left", 16 | "right": "right" 17 | } 18 | }, { 19 | "type": "gamepad", 20 | "config": { 21 | "index": 0, 22 | "buttons": { 23 | "0": "b", 24 | "1": "a", 25 | "2": "a", 26 | "3": "b", 27 | "8": "select", 28 | "9": "start" 29 | }, 30 | "axes": { 31 | "0": "horizontal", 32 | "1": "vertical", 33 | "4": "horizontal", 34 | "5": "vertical" 35 | } 36 | } 37 | } 38 | ] 39 | }, 40 | { 41 | "type": "standard", 42 | "controls": { 43 | "type": "gamepad", 44 | "config": { 45 | "index": 1, 46 | "buttons": { 47 | "0": "b", 48 | "1": "a", 49 | "2": "a", 50 | "3": "b", 51 | "8": "select", 52 | "9": "start" 53 | }, 54 | "axes": { 55 | "0": "horizontal", 56 | "1": "vertical", 57 | "4": "horizontal", 58 | "5": "vertical" 59 | } 60 | } 61 | } 62 | } 63 | ] 64 | } 65 | -------------------------------------------------------------------------------- /test/apu/source/common/sync_dmc.s: -------------------------------------------------------------------------------- 1 | .align 64 2 | 3 | ; Synchronizes to DMC timer within 7 clocks. 4 | ; Preserved: A, X, Y 5 | ; Time: ~1000 clocks max 6 | sync_dmc_fast: 7 | pha 8 | 9 | lda #0 10 | sta $4013 11 | lda #$0F 12 | sta $4010 13 | sta SNDCHN 14 | 15 | ; Start twice (first will clear immediately) 16 | lda #$1F 17 | sta SNDCHN 18 | nop 19 | sta SNDCHN 20 | 21 | lda #$10 22 | : bit SNDCHN 23 | bne :- 24 | 25 | pla 26 | rts 27 | 28 | 29 | ; Synchronizes precisely with DMC timer. Leaves DMC at 30 | ; maximum rate, length 0 (1 byte). To avoid stopping 31 | ; other sound channels when writing to SNDCHN, it 32 | ; writes $1F rather than $10. 33 | ; Preserved: A, X, Y 34 | ; Time: ~680-3800 clocks 35 | sync_dmc: 36 | jsr sync_dmc_fast 37 | 38 | pha 39 | 40 | ; DO NOT write to memory. It affects timing. 41 | 42 | nop ; 6 fine-tune: 6=OK 7=fail 43 | nop 44 | nop 45 | lda #55 ; 386 delay for first iter 46 | bne :+ ; 3 47 | 48 | ; Fine synchronize. 433 clocks per iter. 49 | @sync: lda #59 ; 414 delay 50 | : sec 51 | sbc #1 52 | bne :- 53 | ; 4 DMC wait-states 54 | lda #$1F ; 2 55 | sta SNDCHN ; 4 56 | lda #$10 ; 2 57 | bit SNDCHN ; 4 58 | bne @sync ; 3 59 | 60 | pla 61 | rts 62 | -------------------------------------------------------------------------------- /test/ppu/source/09-even_odd_frames.s: -------------------------------------------------------------------------------- 1 | ; Tests clock skipped on every other PPU frame when BG rendering 2 | ; is enabled. 3 | ; 4 | ; Tries pattern of BG enabled/disabled during a sequence of 5 | ; 5 frames, then finds how many clocks were skipped. Prints 6 | ; number skipped clocks to help find problems. 7 | ; 8 | ; Correct output: 00 01 01 02 9 | 10 | .include "shell.inc" 11 | .include "sync_vbl.s" 12 | 13 | test: pha 14 | lda #0 15 | sta $2001 16 | jsr sync_vbl 17 | delay 29755 18 | pla 19 | 20 | ; Enable/disable rendering for each frame 21 | ; based on corresponding bit in A. 22 | ldx #5 23 | : pha 24 | and #$08 25 | sta $2001 26 | delay 29781-3-2-4-4-2-2-3 27 | pla 28 | lsr a 29 | dex 30 | bne :- 31 | 32 | ; Find number of PPU clocks skipped 33 | lda #0 34 | sta $2001 35 | ldx #6 36 | : delay 29781-2-4-3 37 | dex 38 | bit PPUSTATUS 39 | bpl :- 40 | 41 | jsr print_x 42 | rts 43 | 44 | .macro test pattern,time,code,text 45 | set_test code,text 46 | lda #8*pattern 47 | jsr test 48 | cpx #time 49 | jne test_failed 50 | .endmacro 51 | 52 | main: test %00000,0,2,"Pattern ----- should not skip any clocks" 53 | test %00011,1,3,"Pattern ---BB should skip 1 clock" 54 | test %01001,1,4,"Pattern -B--B (even, odd) should skip 1 clock" 55 | test %11011,2,5,"Pattern BB-BB (two pairs) should skip 2 clocks" 56 | jmp tests_passed 57 | -------------------------------------------------------------------------------- /test/apu/source/common/text_out.s: -------------------------------------------------------------------------------- 1 | ; Text output as expanding zero-terminated string at text_out_base 2 | 3 | ; The final exit result byte is written here 4 | final_result = $6000 5 | 6 | ; Text output is written here as an expanding 7 | ; zero-terminated string 8 | text_out_base = $6004 9 | 10 | bss_res text_out_temp 11 | zp_res text_out_addr,2 12 | 13 | init_text_out: 14 | ldx #0 15 | 16 | ; Put valid data first 17 | setb text_out_base,0 18 | 19 | lda #$80 20 | jsr set_final_result 21 | 22 | ; Now fill in signature that tells emulator there's 23 | ; useful data there 24 | setb text_out_base-3,$DE 25 | setb text_out_base-2,$B0 26 | setb text_out_base-1,$61 27 | 28 | ldx #>text_out_base 29 | stx text_out_addr+1 30 | setb text_out_addr,text_out_base 29 | stx text_out_addr+1 30 | setb text_out_addr,text_out_base 29 | stx text_out_addr+1 30 | setb text_out_addr,> 6; 30 | } else { 31 | otherBit = ( this.shift & 0x2 ) >> 1; 32 | } 33 | 34 | feedback = ( this.shift ^ otherBit ) & 1; 35 | 36 | this.shift >>>= 1; 37 | this.shift |= ( feedback << 14 ); 38 | 39 | if ( this.lengthCounter && !( this.shift & 1 ) ) { 40 | this.sample = this.masterVolume; 41 | } else { 42 | this.sample = 0; 43 | } 44 | 45 | this.timer += this.timerMax; 46 | } 47 | } 48 | }; 49 | 50 | Noise.prototype.writeRegister = function( index, value ) { 51 | switch ( index ) { 52 | case 0: 53 | // set envelope 54 | this.setEnvelope( value ); 55 | 56 | break; 57 | case 1: 58 | // unused 59 | break; 60 | case 2: 61 | // set mode and timer period 62 | this.mode = ( value & 0x80 ) >>> 7; 63 | this.timerMax = this.timer = periodLookup[ value & 15 ]; 64 | 65 | break; 66 | case 3: 67 | // set length counter load and restart envelope 68 | this.setLengthCounter( value >>> 3 ); 69 | this.envelopeStart = true; 70 | 71 | break; 72 | } 73 | }; 74 | 75 | var periodLookup = [ 76 | 4, 8, 16, 32, 64, 96, 128, 160, 202, 254, 380, 508, 762, 1016, 2034, 4068 77 | ]; // TODO support PAL 78 | 79 | module.exports = Noise; -------------------------------------------------------------------------------- /test/apu/source/5-len_timing.s: -------------------------------------------------------------------------------- 1 | ; Verifies timing of length counter clocks in both modes 2 | 3 | .include "apu_shell.inc" 4 | 5 | main: test_main_chans test_chan 6 | jmp tests_passed 7 | 8 | .macro test len,clk,mode,time 9 | jsr sync_apu 10 | delay 3 11 | setb {$4003,x},len 12 | setb SNDMODE,clk*$C0 ; optionally clock length 13 | lda #mode 14 | sta SNDMODE ; begin mode 15 | delay time-4 16 | lda SNDCHN ; read at time 17 | and chan_bit 18 | .endmacro 19 | 20 | test_chan: 21 | set_test 2,"First length of mode 0 is too soon" 22 | test len_2,1,0,14915 23 | jeq chan_failed 24 | 25 | set_test 3,"First length of mode 0 is too late" 26 | test len_2,1,0,14916 27 | jne chan_failed 28 | 29 | set_test 4,"Second length of mode 0 is too soon" 30 | test len_2,0,0,29831 31 | jeq chan_failed 32 | 33 | set_test 5,"Second length of mode 0 is too late" 34 | test len_2,0,0,29832 35 | jne chan_failed 36 | 37 | set_test 6,"Third length of mode 0 is too soon" 38 | test len_4,1,0,44745 39 | jeq chan_failed 40 | 41 | set_test 7,"Third length of mode 0 is too late" 42 | test len_4,1,0,44746 43 | jne chan_failed 44 | 45 | 46 | set_test 8,"First length of mode 1 is too soon" 47 | test len_2,0,$80,14915 48 | jeq chan_failed 49 | 50 | set_test 9,"First length of mode 1 is too late" 51 | test len_2,0,$80,14916 52 | jne chan_failed 53 | 54 | set_test 10,"Second length of mode 1 is too soon" 55 | test len_4,1,$80,37283 56 | jeq chan_failed 57 | 58 | set_test 11,"Second length of mode 1 is too late" 59 | test len_4,1,$80,37284 60 | jne chan_failed 61 | 62 | set_test 12,"Third length of mode 1 is too soon" 63 | test len_4,0,$80,52197 64 | jeq chan_failed 65 | 66 | set_test 13,"Third length of mode 1 is too late" 67 | test len_4,0,$80,52198 68 | jne chan_failed 69 | 70 | rts 71 | -------------------------------------------------------------------------------- /test/cpu_interrupts/source/4-irq_and_dma.s: -------------------------------------------------------------------------------- 1 | ; Has IRQ occur at various times around sprite DMA. 2 | ; First column refers to what instruction IRQ occurred 3 | ; after. Second column is time of IRQ, in CPU clocks relative 4 | ; to some arbitrary starting point. 5 | ; 6 | ; 0 +0 7 | ; 1 +1 8 | ; 1 +2 9 | ; 2 +3 10 | ; 2 +4 11 | ; 4 +5 12 | ; 4 +6 13 | ; 7 +7 14 | ; 7 +8 15 | ; 7 +9 16 | ; 7 +10 17 | ; 8 +11 18 | ; 8 +12 19 | ; 8 +13 20 | ; ... 21 | ; 8 +524 22 | ; 8 +525 23 | ; 8 +526 24 | ; 9 +527 25 | 26 | CUSTOM_IRQ=1 27 | .include "shell.inc" 28 | .include "sync_apu.s" 29 | 30 | irq: bit SNDCHN 31 | rti 32 | 33 | begin: 34 | jsr sync_apu 35 | lda #0 36 | sta SNDMODE 37 | cli 38 | delay 29273 39 | lda #0 40 | rts 41 | 42 | end: tsx 43 | dex 44 | lda #$80 45 | sta $100,x 46 | landing: 47 | ; second column refers to these: 48 | nop ; 0 49 | nop ; 1 50 | lda #$07 ; 2 51 | sta SPRDMA ; 4 52 | nop ; 7 53 | nop ; 8 54 | nop ; 9 55 | sei 56 | lda $100,x 57 | sec 58 | sbc #>> 3 ); 67 | this.linearCounterStart = true; 68 | 69 | break; 70 | } 71 | }; 72 | 73 | var sequence = [ 74 | 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 75 | 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 76 | ]; 77 | 78 | module.exports = Triangle; -------------------------------------------------------------------------------- /test/sprite_overflow/source/ppu_util.a: -------------------------------------------------------------------------------- 1 | 2 | ; Clear VBL flag then wait for it to be set 3 | ; Preserved: A, X, Y 4 | wait_vbl: 5 | bit $2002 6 | : bit $2002 7 | bpl - 8 | rts 9 | .code 10 | 11 | ; Set VRAM address to A * $100 12 | ; Preserved: X, Y 13 | set_vpage: 14 | bit $2002 15 | sta $2006 16 | lda #0 17 | sta $2006 18 | rts 19 | .code 20 | 21 | ; Set VRAM address to A * $100 + X 22 | ; Preserved: A, X, Y 23 | set_vaddr: 24 | bit $2002 25 | sta $2006 26 | stx $2006 27 | rts 28 | .code 29 | 30 | ; Set X and Y scroll 31 | ; Preserved: A, X, Y 32 | set_vscroll: 33 | bit $2002 34 | stx $2005 35 | sty $2005 36 | rts 37 | .code 38 | 39 | ; Turn off NMI and disable BG and sprites 40 | ; Preserved: A, X, Y 41 | disable_ppu: 42 | pha 43 | lda #0 44 | sta $2000 45 | sta $2001 46 | bit $2002 47 | sta $2006 48 | sta $2006 49 | pla 50 | rts 51 | .code 52 | 53 | ; Set sprite memory to $ff 54 | ; Preserved: Y 55 | clear_sprites: 56 | lda #$ff 57 | ldx #0 58 | : sta $2004 59 | dex 60 | bne - 61 | rts 62 | .code 63 | 64 | ; Clear/fill nametable with 0/A and clear attributes to 0 65 | clear_nametable: 66 | lda #0 67 | fill_nametable: 68 | pha 69 | lda #$20 70 | jsr set_vpage 71 | pla 72 | ldx #240 73 | : sta $2007 74 | sta $2007 75 | sta $2007 76 | sta $2007 77 | dex 78 | bne - 79 | lda #0 80 | ldx #64 81 | : sta $2007 82 | dex 83 | bne - 84 | rts 85 | .code 86 | 87 | ; Clear/fill VRAM with 0/A 88 | clear_vram: 89 | lda #0 90 | fill_vram: 91 | ldx #0 92 | ldy #$24 93 | bne fill_vram_ 94 | 95 | ; Clear/fill CHR with 0/A 96 | fill_chr1: 97 | ldx #$10 98 | ldy #$10 99 | bne fill_vram_ 100 | 101 | fill_chr0: 102 | ldx #0 103 | ldy #$10 104 | bne fill_vram_ 105 | 106 | clear_chr: 107 | lda #0 108 | fill_chr: 109 | ldx #0 110 | ldy #$20 111 | ; Fill VRAM Y*$100 bytes of VRAM with A, starting at X*$100 112 | fill_vram_: 113 | bit $2002 114 | stx $2006 115 | ldx #0 116 | stx $2006 117 | : sta $2007 118 | dex 119 | bne - 120 | dey 121 | bne - 122 | rts 123 | .code 124 | -------------------------------------------------------------------------------- /test/ppu/source/common/crc.s: -------------------------------------------------------------------------------- 1 | ; CRC-32 checksum calculation 2 | 3 | zp_res checksum,4 ; Current CRC-32; no need to invert 4 | zp_byte checksum_temp 5 | zp_byte checksum_off_ 6 | 7 | ; Turns CRC updating on/off. Allows nesting. 8 | ; Preserved: A, X, Y 9 | crc_off: 10 | dec checksum_off_ 11 | rts 12 | 13 | crc_on: inc checksum_off_ 14 | beq :+ 15 | jpl internal_error ; catch unbalanced crc calls 16 | : rts 17 | 18 | 19 | ; Initializes checksum module. Might initialize tables 20 | ; in the future. 21 | init_crc: 22 | jmp reset_crc 23 | 24 | 25 | ; Clears checksum and turns it on 26 | ; Preserved: X, Y 27 | reset_crc: 28 | lda #0 29 | sta checksum_off_ 30 | sta checksum 31 | sta checksum + 1 32 | sta checksum + 2 33 | sta checksum + 3 34 | rts 35 | 36 | 37 | ; Updates checksum with byte in A (unless disabled via crc_off) 38 | ; Preserved: A, X, Y 39 | ; Time: 360 clocks average 40 | update_crc: 41 | bit checksum_off_ 42 | bmi update_crc_off 43 | update_crc_: 44 | pha 45 | stx checksum_temp 46 | eor checksum 47 | ldx #8 48 | sec 49 | @bit: ror checksum+3 50 | ror checksum+2 51 | ror checksum+1 52 | ror a 53 | bcs :+ 54 | sta checksum 55 | lda checksum+3 56 | eor #$ED 57 | sta checksum+3 58 | lda checksum+2 59 | eor #$B8 60 | sta checksum+2 61 | lda checksum+1 62 | eor #$83 63 | sta checksum+1 64 | lda checksum 65 | eor #$20 66 | sec 67 | : dex 68 | bne @bit 69 | sta checksum 70 | ldx checksum_temp 71 | pla 72 | update_crc_off: 73 | rts 74 | 75 | 76 | ; Prints checksum as 8-character hex value 77 | print_crc: 78 | jsr crc_off 79 | 80 | ldx #3 81 | : lda checksum,x 82 | jsr print_hex 83 | dex 84 | bpl :- 85 | 86 | jmp crc_on 87 | 88 | 89 | ; EQ if checksum matches CRC 90 | ; Out: A=0 and EQ if match, A>0 and NE if different 91 | ; Preserved: X, Y 92 | .macro is_crc crc 93 | jsr_with_addr is_crc_,{.dword crc} 94 | .endmacro 95 | 96 | is_crc_: 97 | tya 98 | pha 99 | 100 | ldy #3 101 | : lda (ptr),y 102 | cmp checksum,y 103 | bne @wrong 104 | dey 105 | bpl :- 106 | pla 107 | tay 108 | lda #0 109 | rts 110 | 111 | @wrong: 112 | pla 113 | tay 114 | lda #1 115 | rts 116 | -------------------------------------------------------------------------------- /test/apu/source/common/crc.s: -------------------------------------------------------------------------------- 1 | ; CRC-32 checksum calculation 2 | 3 | zp_res checksum,4 4 | zp_byte checksum_temp 5 | zp_byte checksum_off_ 6 | 7 | ; Turns CRC updating on/off. Allows nesting. 8 | ; Preserved: A, X, Y 9 | crc_off: 10 | dec checksum_off_ 11 | rts 12 | 13 | crc_on: inc checksum_off_ 14 | beq :+ 15 | jpl internal_error ; catch unbalanced crc calls 16 | : rts 17 | 18 | 19 | ; Initializes checksum module. Might initialize tables 20 | ; in the future. 21 | init_crc: 22 | jmp reset_crc 23 | 24 | 25 | ; Clears checksum and turns it on 26 | ; Preserved: X, Y 27 | reset_crc: 28 | lda #0 29 | sta checksum_off_ 30 | lda #$FF 31 | sta checksum 32 | sta checksum + 1 33 | sta checksum + 2 34 | sta checksum + 3 35 | rts 36 | 37 | 38 | ; Updates checksum with byte in A (unless disabled via crc_off) 39 | ; Preserved: A, X, Y 40 | ; Time: 357 clocks average 41 | update_crc: 42 | bit checksum_off_ 43 | bmi update_crc_off 44 | update_crc_: 45 | pha 46 | stx checksum_temp 47 | eor checksum 48 | ldx #8 49 | @bit: lsr checksum+3 50 | ror checksum+2 51 | ror checksum+1 52 | ror a 53 | bcc :+ 54 | sta checksum 55 | lda checksum+3 56 | eor #$ED 57 | sta checksum+3 58 | lda checksum+2 59 | eor #$B8 60 | sta checksum+2 61 | lda checksum+1 62 | eor #$83 63 | sta checksum+1 64 | lda checksum 65 | eor #$20 66 | : dex 67 | bne @bit 68 | sta checksum 69 | ldx checksum_temp 70 | pla 71 | update_crc_off: 72 | rts 73 | 74 | 75 | ; Prints checksum as 8-character hex value 76 | print_crc: 77 | jsr crc_off 78 | 79 | ; Print complement 80 | ldx #3 81 | : lda checksum,x 82 | eor #$FF 83 | jsr print_hex 84 | dex 85 | bpl :- 86 | 87 | jmp crc_on 88 | 89 | 90 | ; EQ if checksum matches CRC 91 | ; Out: A=0 and EQ if match, A>0 and NE if different 92 | ; Preserved: X, Y 93 | .macro is_crc crc 94 | jsr_with_addr is_crc_,{.dword crc} 95 | .endmacro 96 | 97 | is_crc_: 98 | tya 99 | pha 100 | 101 | ; Compare with complemented checksum 102 | ldy #3 103 | : lda (ptr),y 104 | sec 105 | adc checksum,y 106 | bne @wrong 107 | dey 108 | bpl :- 109 | pla 110 | tay 111 | lda #0 112 | rts 113 | 114 | @wrong: 115 | pla 116 | tay 117 | lda #1 118 | rts 119 | -------------------------------------------------------------------------------- /test/cpu_interrupts/source/common/crc.s: -------------------------------------------------------------------------------- 1 | ; CRC-32 checksum calculation 2 | 3 | zp_res checksum,4 4 | zp_byte checksum_temp 5 | zp_byte checksum_off_ 6 | 7 | ; Turns CRC updating on/off. Allows nesting. 8 | ; Preserved: A, X, Y 9 | crc_off: 10 | dec checksum_off_ 11 | rts 12 | 13 | crc_on: inc checksum_off_ 14 | beq :+ 15 | jpl internal_error ; catch unbalanced crc calls 16 | : rts 17 | 18 | 19 | ; Initializes checksum module. Might initialize tables 20 | ; in the future. 21 | init_crc: 22 | jmp reset_crc 23 | 24 | 25 | ; Clears checksum and turns it on 26 | ; Preserved: X, Y 27 | reset_crc: 28 | lda #0 29 | sta checksum_off_ 30 | lda #$FF 31 | sta checksum 32 | sta checksum + 1 33 | sta checksum + 2 34 | sta checksum + 3 35 | rts 36 | 37 | 38 | ; Updates checksum with byte in A (unless disabled via crc_off) 39 | ; Preserved: A, X, Y 40 | ; Time: 357 clocks average 41 | update_crc: 42 | bit checksum_off_ 43 | bmi update_crc_off 44 | update_crc_: 45 | pha 46 | stx checksum_temp 47 | eor checksum 48 | ldx #8 49 | @bit: lsr checksum+3 50 | ror checksum+2 51 | ror checksum+1 52 | ror a 53 | bcc :+ 54 | sta checksum 55 | lda checksum+3 56 | eor #$ED 57 | sta checksum+3 58 | lda checksum+2 59 | eor #$B8 60 | sta checksum+2 61 | lda checksum+1 62 | eor #$83 63 | sta checksum+1 64 | lda checksum 65 | eor #$20 66 | : dex 67 | bne @bit 68 | sta checksum 69 | ldx checksum_temp 70 | pla 71 | update_crc_off: 72 | rts 73 | 74 | 75 | ; Prints checksum as 8-character hex value 76 | print_crc: 77 | jsr crc_off 78 | 79 | ; Print complement 80 | ldx #3 81 | : lda checksum,x 82 | eor #$FF 83 | jsr print_hex 84 | dex 85 | bpl :- 86 | 87 | jmp crc_on 88 | 89 | 90 | ; EQ if checksum matches CRC 91 | ; Out: A=0 and EQ if match, A>0 and NE if different 92 | ; Preserved: X, Y 93 | .macro is_crc crc 94 | jsr_with_addr is_crc_,{.dword crc} 95 | .endmacro 96 | 97 | is_crc_: 98 | tya 99 | pha 100 | 101 | ; Compare with complemented checksum 102 | ldy #3 103 | : lda (ptr),y 104 | sec 105 | adc checksum,y 106 | bne @wrong 107 | dey 108 | bpl :- 109 | pla 110 | tay 111 | lda #0 112 | rts 113 | 114 | @wrong: 115 | pla 116 | tay 117 | lda #1 118 | rts 119 | -------------------------------------------------------------------------------- /test/cpu_interrupts/source/2-nmi_and_brk.s: -------------------------------------------------------------------------------- 1 | ; NMI behavior when it interrupts BRK. Occasionally fails on 2 | ; NES due to PPU-CPU synchronization. 3 | ; 4 | ; Result when run: 5 | ; NMI BRK -- 6 | ; 27 36 00 NMI before CLC 7 | ; 26 36 00 NMI after CLC 8 | ; 26 36 00 9 | ; 36 00 00 NMI interrupting BRK, with B bit set on stack 10 | ; 36 00 00 11 | ; 36 00 00 12 | ; 36 00 00 13 | ; 36 00 00 14 | ; 27 36 00 NMI after SEC at beginning of IRQ handler 15 | ; 27 36 00 16 | 17 | CUSTOM_IRQ=1 18 | CUSTOM_NMI=1 19 | .include "shell.inc" 20 | .include "sync_vbl.s" 21 | 22 | zp_byte irq_flag ; IRQ sets this to saved flags on stack 23 | zp_byte irq_temp 24 | 25 | zp_byte nmi_flag ; NMI sets this to saved flags on stack 26 | zp_byte nmi_temp 27 | 28 | nmi: ; 7 29 | sta = 2, print name 70 | cpx #2-1 ; -1 due to inx above 71 | blt :+ 72 | lda test_name+1 73 | beq :+ 74 | jsr print_newline 75 | sta addr+1 76 | lda test_name 77 | sta addr 78 | jsr print_str_addr 79 | jsr print_newline 80 | : 81 | jsr print_filename 82 | 83 | ; End program 84 | lda test_code 85 | jmp exit 86 | 87 | 88 | ; If checksum doesn't match expected, reports failed test. 89 | ; Clears checksum afterwards. 90 | ; Preserved: A, X, Y 91 | .macro check_crc expected 92 | jsr_with_addr check_crc_,{.dword expected} 93 | .endmacro 94 | 95 | check_crc_: 96 | pha 97 | jsr is_crc_ 98 | bne :+ 99 | jsr reset_crc 100 | pla 101 | rts 102 | 103 | : jsr print_newline 104 | jsr print_crc 105 | jmp test_failed 106 | -------------------------------------------------------------------------------- /test/ppu/source/common/testing.s: -------------------------------------------------------------------------------- 1 | ; Utilities for writing test ROMs 2 | 3 | ; In NVRAM so these can be used before initializing runtime, 4 | ; then runtime initialized without clearing them 5 | nv_res test_code ; code of current test 6 | nv_res test_name,2 ; address of name of current test, or 0 of none 7 | 8 | 9 | ; Sets current test code and optional name. Also resets 10 | ; checksum. 11 | ; Preserved: A, X, Y 12 | .macro set_test code,name 13 | pha 14 | lda #code 15 | jsr set_test_ 16 | .ifblank name 17 | setb test_name+1,0 18 | .else 19 | .local Addr 20 | setw test_name,Addr 21 | seg_data RODATA,{Addr: .byte name,0} 22 | .endif 23 | pla 24 | .endmacro 25 | 26 | set_test_: 27 | sta test_code 28 | jmp reset_crc 29 | 30 | 31 | ; Initializes testing module 32 | init_testing = init_crc 33 | 34 | 35 | ; Reports that all tests passed 36 | tests_passed: 37 | jsr print_filename 38 | print_str newline,"Passed" 39 | lda #0 40 | jmp exit 41 | 42 | 43 | ; Reports "Done" if set_test has never been used, 44 | ; "Passed" if set_test 0 was last used, or 45 | ; failure if set_test n was last used. 46 | tests_done: 47 | ldx test_code 48 | jeq tests_passed 49 | inx 50 | bne test_failed 51 | jsr print_filename 52 | print_str newline,"Done" 53 | lda #0 54 | jmp exit 55 | 56 | 57 | ; Reports that the current test failed. Prints code and 58 | ; name last set with set_test, or just "Failed" if none 59 | ; have been set yet. 60 | test_failed: 61 | ldx test_code 62 | 63 | ; Treat $FF as 1, in case it wasn't ever set 64 | inx 65 | bne :+ 66 | inx 67 | stx test_code 68 | : 69 | ; If code >= 2, print name 70 | cpx #2-1 ; -1 due to inx above 71 | blt :+ 72 | lda test_name+1 73 | beq :+ 74 | jsr print_newline 75 | sta addr+1 76 | lda test_name 77 | sta addr 78 | jsr print_str_addr 79 | jsr print_newline 80 | : 81 | jsr print_filename 82 | 83 | ; End program 84 | lda test_code 85 | jmp exit 86 | 87 | 88 | ; If checksum doesn't match expected, reports failed test. 89 | ; Clears checksum afterwards. 90 | ; Preserved: A, X, Y 91 | .macro check_crc expected 92 | jsr_with_addr check_crc_,{.dword expected} 93 | .endmacro 94 | 95 | check_crc_: 96 | pha 97 | jsr is_crc_ 98 | bne :+ 99 | jsr reset_crc 100 | pla 101 | rts 102 | 103 | : jsr print_newline 104 | jsr print_crc 105 | jmp test_failed 106 | -------------------------------------------------------------------------------- /test/sprite_overflow/source/debug.a: -------------------------------------------------------------------------------- 1 | 2 | ; Beep A times. Made to require minimal features from APU. 3 | debug_beeps: 4 | beep_loop: 5 | pha 6 | 7 | lda #1 ; set up square 1 8 | sta $4015 9 | sta $4003 10 | sta $4001 11 | sta $4002 12 | 13 | lda #$0f ; fade volume 14 | : pha 15 | eor #$30 16 | sta $4000 17 | lda #8 18 | jsr delay_msec 19 | pla 20 | clc 21 | adc #-1 22 | bpl - 23 | 24 | lda #0 25 | sta $4015 ; silence square for a bit 26 | lda #120 27 | jsr delay_msec 28 | 29 | pla 30 | clc 31 | adc #-1 32 | bne beep_loop 33 | rts 34 | .code 35 | 36 | ; Print indicated register to console as $hh 37 | ; Preserved: A, X, Y, P 38 | print_a: 39 | php 40 | jsr debug_byte 41 | plp 42 | rts 43 | .code 44 | 45 | print_x: 46 | php 47 | pha 48 | txa 49 | jsr debug_byte 50 | pla 51 | plp 52 | rts 53 | .code 54 | 55 | print_y: 56 | php 57 | pha 58 | tya 59 | jsr debug_byte 60 | pla 61 | plp 62 | rts 63 | .code 64 | 65 | print_s: 66 | php 67 | pha 68 | txa 69 | pha 70 | tsx 71 | txa 72 | jsr debug_byte 73 | pla 74 | tax 75 | pla 76 | plp 77 | rts 78 | .code 79 | 80 | print_p: 81 | php 82 | pha 83 | php 84 | pla 85 | jsr debug_byte 86 | pla 87 | plp 88 | rts 89 | .code 90 | 91 | print_ay: 92 | php 93 | pha 94 | lda #36 95 | jsr debug_char 96 | pla 97 | pha 98 | jsr hex_byte 99 | tya 100 | jsr hex_byte 101 | lda #32 ; ' ' 102 | jsr debug_char_no_wait 103 | pla 104 | plp 105 | rts 106 | .code 107 | 108 | print_newline: 109 | jmp debug_newline 110 | .code 111 | 112 | ; Print address YA to console as $hhhh 113 | ; Preserved: A, X, Y 114 | debug_addr: 115 | pha 116 | lda #36 ; '$' 117 | jsr debug_char 118 | tya 119 | jsr hex_byte 120 | jmp debug_byte_impl 121 | .code 122 | 123 | ; Print byte A to console as $hh 124 | ; Preserved: A, X, Y 125 | debug_byte: 126 | pha 127 | lda #36 ; '$' 128 | jsr debug_char 129 | debug_byte_impl: 130 | pla 131 | pha 132 | jsr hex_byte 133 | lda #32 ; ' ' 134 | jsr debug_char_no_wait 135 | pla 136 | rts 137 | 138 | hex_byte: 139 | pha 140 | lsr a 141 | lsr a 142 | lsr a 143 | lsr a 144 | jsr nybble 145 | pla 146 | and #$0f 147 | nybble: 148 | cmp #10 149 | bcc not_letter 150 | adc #6 ; relies on carry being set 151 | not_letter: 152 | adc #$30 153 | jmp debug_char_no_wait 154 | .code 155 | -------------------------------------------------------------------------------- /test/cpu_interrupts/source/common/testing.s: -------------------------------------------------------------------------------- 1 | ; Utilities for writing test ROMs 2 | 3 | ; In NVRAM so these can be used before initializing runtime, 4 | ; then runtime initialized without clearing them 5 | nv_res test_code ; code of current test 6 | nv_res test_name,2 ; address of name of current test, or 0 of none 7 | 8 | 9 | ; Sets current test code and optional name. Also resets 10 | ; checksum. 11 | ; Preserved: A, X, Y 12 | .macro set_test code,name 13 | pha 14 | lda #code 15 | jsr set_test_ 16 | .ifblank name 17 | setb test_name+1,0 18 | .else 19 | .local Addr 20 | setw test_name,Addr 21 | seg_data "RODATA",{Addr: .byte name,0} 22 | .endif 23 | pla 24 | .endmacro 25 | 26 | set_test_: 27 | sta test_code 28 | jmp reset_crc 29 | 30 | 31 | ; Initializes testing module 32 | init_testing = init_crc 33 | 34 | 35 | ; Reports that all tests passed 36 | tests_passed: 37 | jsr print_filename 38 | print_str newline,"Passed" 39 | lda #0 40 | jmp exit 41 | 42 | 43 | ; Reports "Done" if set_test has never been used, 44 | ; "Passed" if set_test 0 was last used, or 45 | ; failure if set_test n was last used. 46 | tests_done: 47 | ldx test_code 48 | jeq tests_passed 49 | inx 50 | bne test_failed 51 | jsr print_filename 52 | print_str newline,"Done" 53 | lda #0 54 | jmp exit 55 | 56 | 57 | ; Reports that the current test failed. Prints code and 58 | ; name last set with set_test, or just "Failed" if none 59 | ; have been set yet. 60 | test_failed: 61 | ldx test_code 62 | 63 | ; Treat $FF as 1, in case it wasn't ever set 64 | inx 65 | bne :+ 66 | inx 67 | stx test_code 68 | : 69 | ; If code >= 2, print name 70 | cpx #2-1 ; -1 due to inx above 71 | blt :+ 72 | lda test_name+1 73 | beq :+ 74 | jsr print_newline 75 | sta addr+1 76 | lda test_name 77 | sta addr 78 | jsr print_str_addr 79 | jsr print_newline 80 | : 81 | jsr print_filename 82 | 83 | ; End program 84 | lda test_code 85 | jmp exit 86 | 87 | 88 | ; If checksum doesn't match expected, reports failed test. 89 | ; Clears checksum afterwards. 90 | ; Preserved: A, X, Y 91 | .macro check_crc expected 92 | jsr_with_addr check_crc_,{.dword expected} 93 | .endmacro 94 | 95 | check_crc_: 96 | pha 97 | jsr is_crc_ 98 | bne :+ 99 | jsr reset_crc 100 | pla 101 | rts 102 | 103 | : jsr print_newline 104 | jsr print_crc 105 | jmp test_failed 106 | -------------------------------------------------------------------------------- /test/cpu_interrupts/source/3-nmi_and_irq.s: -------------------------------------------------------------------------------- 1 | ; NMI behavior when it interrupts IRQ vectoring. 2 | ; 3 | ; Result when run: 4 | ; NMI IRQ 5 | ; 23 00 NMI occurs before LDA #1 6 | ; 21 00 NMI occurs after LDA #1 (Z flag clear) 7 | ; 21 00 8 | ; 20 00 NMI occurs after CLC, interrupting IRQ 9 | ; 20 00 10 | ; 20 00 11 | ; 20 00 12 | ; 20 00 13 | ; 20 00 14 | ; 20 00 Same result for 7 clocks before IRQ is vectored 15 | ; 25 20 IRQ occurs, then NMI occurs after SEC in IRQ handler 16 | ; 25 20 17 | 18 | CUSTOM_IRQ=1 19 | CUSTOM_NMI=1 20 | .include "shell.inc" 21 | .include "sync_vbl.s" 22 | 23 | zp_byte irq_flag ; IRQ sets this to saved flags on stack 24 | zp_byte irq_temp 25 | 26 | zp_byte nmi_flag ; NMI sets this to saved flags on stack 27 | zp_byte nmi_temp 28 | 29 | nmi: ; 7 30 | sta (swap to e-mail) 17 | 18 | 19 | palette_ram 20 | ----------- 21 | PPU palette RAM read/write and mirroring test 22 | 23 | 1) Tests passed 24 | 2) Palette read shouldn't be buffered like other VRAM 25 | 3) Palette write/read doesn't work 26 | 4) Palette should be mirrored within $3f00-$3fff 27 | 5) Write to $10 should be mirrored at $00 28 | 6) Write to $00 should be mirrored at $10 29 | 30 | 31 | power_up_palette 32 | ---------------- 33 | Reports whether initial values in palette at power-up match those 34 | that my NES has. These values are probably unique to my NES. 35 | 36 | 1) Palette matches 37 | 2) Palette differs from table 38 | 39 | 40 | sprite_ram 41 | ---------- 42 | Tests sprite RAM access via $2003, $2004, and $4014 43 | 44 | 1) Tests passed 45 | 2) Basic read/write doesn't work 46 | 3) Address should increment on $2004 write 47 | 4) Address should not increment on $2004 read 48 | 5) Third sprite bytes should be masked with $e3 on read 49 | 6) $4014 DMA copy doesn't work at all 50 | 7) $4014 DMA copy should start at value in $2003 and wrap 51 | 8) $4014 DMA copy should leave value in $2003 intact 52 | 53 | 54 | vbl_clear_time 55 | -------------- 56 | The VBL flag ($2002.7) is cleared by the PPU around 2270 CPU clocks 57 | after NMI occurs. 58 | 59 | 1) Tests passed 60 | 2) VBL flag cleared too soon 61 | 3) VBL flag cleared too late 62 | 63 | 64 | vram_access 65 | ----------- 66 | Tests PPU VRAM read/write and internal read buffer operation 67 | 68 | 1) Tests passed 69 | 2) VRAM reads should be delayed in a buffer 70 | 3) Basic Write/read doesn't work 71 | 4) Read buffer shouldn't be affected by VRAM write 72 | 5) Read buffer shouldn't be affected by palette write 73 | 6) Palette read should also read VRAM into read buffer 74 | 7) "Shadow" VRAM read unaffected by palette transparent color mirroring 75 | -------------------------------------------------------------------------------- /test/sprite_overflow/source/delays.a: -------------------------------------------------------------------------------- 1 | ; to do: delay loops that take only a single count 2 | 3 | ; Delay for almost A milliseconds (A * 0.999009524 msec) 4 | ; Preserved: X, Y 5 | delay_msec: 6 | pha ; 3 7 | lda #253 ; 2 8 | sec ; 2 9 | delay_msec_: 10 | nop ; 2 11 | adc #-2 ; 2 12 | bne delay_msec_ ; 3 13 | ; -1 14 | pla ; 4 15 | clc ; 2 16 | adc #-1 ; 2 17 | bne delay_msec ; 3 18 | rts 19 | .code 20 | 21 | ; Delay for almost 'A / 10' milliseconds (A * 0.099453968 msec) 22 | ; Preserved: X, Y 23 | delay_01msec: 24 | pha ; 3 25 | lda #18 ; 2 26 | sec ; 2 27 | delay_01msec_: 28 | nop ; 2 29 | nop ; 2 30 | adc #-2 ; 2 31 | bne delay_01msec_ ; 3 32 | ; -1 33 | pla ; 4 34 | clc ; 2 35 | adc #-1 ; 2 36 | bne delay_01msec ; 3 37 | rts 38 | .code 39 | 40 | ; Delay for almost A*10 milliseconds 41 | ; Preserved: X, Y 42 | delay_10msec: 43 | pha 44 | lda #10 45 | jsr delay_msec 46 | pla 47 | clc 48 | adc #-1 49 | bne delay_10msec 50 | rts 51 | .code 52 | 53 | ; Delay n clocks 54 | ; Preserved: P, A, X, Y 55 | delay_32: nop 56 | delay_30: nop 57 | delay_28: nop 58 | delay_26: nop 59 | delay_24: nop 60 | delay_22: nop 61 | delay_20: nop 62 | delay_18: nop 63 | delay_16: nop 64 | delay_14: nop 65 | delay_12: rts 66 | 67 | delay_33: nop 68 | delay_31: nop 69 | delay_29: nop 70 | delay_27: nop 71 | delay_25: nop 72 | delay_23: nop 73 | delay_21: nop 74 | delay_19: nop 75 | delay_17: beq + ; 5 76 | : bne + 77 | : rts ; 6 78 | .code 79 | 80 | ; Delay (5 * A + 6) * Y + 7 + n clocks 81 | delay_ya11: 82 | .db $a2 ; 1 ldx #imm 83 | delay_ya10: 84 | .db $a2 ; 1 ldx #imm 85 | delay_ya9: 86 | .db $a2 ; 1 ldx #imm 87 | delay_ya8: 88 | .db $a2 ; 1 ldx #imm 89 | delay_ya7: 90 | .db $a2 ; 1 ldx #imm 91 | delay_ya6: 92 | .db $a2 ; 1 ldx #imm 93 | delay_ya5: 94 | .db $a2 ; 1 ldx #imm 95 | delay_ya4: 96 | .db $a2 ; 1 ldx #imm 97 | delay_ya3: 98 | .db $a2 ; 1 ldx #imm 99 | delay_ya2: 100 | .db $a2 ; 1 ldx #imm 101 | delay_ya1: 102 | .db $a6 ; 1 ldx zp 103 | delay_ya0: 104 | nop ; 2 105 | delay_ya: 106 | ; 2 lda # 107 | ; 2 ldy # 108 | ; 6 jsr 109 | tax ; *2 110 | delay_yax: 111 | dex ; **2 112 | bne delay_yax ; **3 113 | ; *-1 114 | dey ; *2 115 | bne delay_ya ; *3 116 | ; -1 117 | rts ; 6 118 | .code 119 | -------------------------------------------------------------------------------- /test/ppu2/source/palette_ram.asm: -------------------------------------------------------------------------------- 1 | ; PPU palette RAM read/write and mirroring test 2 | ; to do: check that upper two bits aren't stored 3 | 4 | .include "prefix_ppu.a" 5 | 6 | ; Set VRAM address to $3f00 + X 7 | ; Preserved: A, X, Y 8 | set_pal_addr: 9 | pha 10 | bit $2002 11 | lda #$3f 12 | sta $2006 13 | txa 14 | sta $2006 15 | pla 16 | rts 17 | 18 | ; Set palette entry X to A 19 | ; Preserved: A, X, Y 20 | set_pal_entry: 21 | jsr set_pal_addr 22 | sta $2007 23 | rts 24 | 25 | ; Read palette entry X into A 26 | ; Preserved: X, Y 27 | get_pal_entry: 28 | jsr set_pal_addr 29 | lda $2007 30 | and #$3f 31 | rts 32 | 33 | reset: 34 | lda #50 35 | jsr delay_msec 36 | 37 | jsr wait_vbl 38 | lda #0 39 | sta $2000 40 | sta $2001 41 | 42 | lda #2;) Palette read shouldn't be buffered like other VRAM 43 | sta result 44 | ldx #$00 45 | lda #$12 46 | jsr set_pal_entry 47 | lda #$34 48 | sta $2007 49 | jsr get_pal_entry 50 | lda $2007 51 | cmp #$12 52 | jsr error_if_eq 53 | 54 | lda #3;) Palette write/read doesn't work 55 | sta result 56 | ldx #$00 57 | lda #$34 58 | jsr set_pal_entry 59 | jsr get_pal_entry 60 | lda $2007 61 | cmp #$34 62 | jsr error_if_ne 63 | 64 | lda #4;) Palette should be mirrored within $3f00-$3fff 65 | sta result 66 | ldx #$00 67 | lda #$12 68 | jsr set_pal_entry 69 | ldx #$e0 70 | lda #$34 71 | jsr set_pal_entry 72 | ldx #$00 73 | jsr get_pal_entry 74 | cmp #$34 75 | jsr error_if_ne 76 | 77 | lda #5;) Write to $10 should be mirrored at $00 78 | sta result 79 | ldx #$00 80 | lda #$12 81 | jsr set_pal_entry 82 | ldx #$10 83 | lda #$34 84 | jsr set_pal_entry 85 | ldx #$00 86 | jsr get_pal_entry 87 | cmp #$34 88 | jsr error_if_ne 89 | 90 | lda #6;) Write to $00 should be mirrored at $10 91 | sta result 92 | ldx #$10 93 | lda #$12 94 | jsr set_pal_entry 95 | ldx #$00 96 | lda #$34 97 | jsr set_pal_entry 98 | ldx #$10 99 | jsr get_pal_entry 100 | cmp #$34 101 | jsr error_if_ne 102 | 103 | lda #1;) Tests passed 104 | sta result 105 | jmp report_final_result 106 | 107 | -------------------------------------------------------------------------------- /system/memory.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | function Memory( system ) { 4 | var i = 0; 5 | 6 | this.system = system; 7 | 8 | this.ram = new Uint8Array( 0x0800 ); 9 | 10 | // initialize RAM to 0xff 11 | for ( ; i < 0x0800; i++ ) { 12 | this.ram[ i ] = 0xff; 13 | } 14 | 15 | this.address = 0; 16 | 17 | this.cartridge = null; 18 | 19 | Object.preventExtensions( this ); 20 | } 21 | 22 | Memory.prototype = { 23 | loadCartridge: function( cartridge ) { 24 | this.cartridge = cartridge; 25 | }, 26 | 27 | readWrite: function( address, write, value ) { 28 | this.address = address; // TODO, do I use this anywhere? 29 | 30 | switch ( address ) { 31 | case 0x4014: 32 | // OAM DMA, write-only 33 | if ( write ) { 34 | var i, base = value << 8; 35 | 36 | for ( i = 0; i < 0x100; i++ ) { 37 | this.write( 0x2004, this.read( base + i ) ); 38 | } 39 | 40 | this.system.cpu.burn(513); 41 | } 42 | return 0; 43 | case 0x4016: 44 | // read controller 1, or write controller strobe 45 | if ( write) { 46 | this.system.controllers.write( value ); 47 | return 0; 48 | } else { 49 | return this.system.controllers.read( 0 ); 50 | } 51 | break; 52 | case 0x4017: 53 | // read controller 2 54 | if ( write ) { 55 | // do nothing, APU frame counter 56 | } else { 57 | return this.system.controllers.read( 1 ); 58 | } 59 | } 60 | 61 | if ( address >= 0x4020 ) { 62 | // address is in cartridge space 63 | if ( write ) { 64 | return this.system.cartridge.writePRG( address, value ); 65 | } else { 66 | return this.system.cartridge.readPRG( address ); 67 | } 68 | } else if ( address < 0x2000 ) { 69 | address &= 0x07ff; 70 | 71 | // RAM 72 | if ( write ) { 73 | this.ram[ address ] = value; 74 | return 0; 75 | } else { 76 | return this.ram[ address ]; 77 | } 78 | } else if ( address < 0x4000 ) { 79 | // PPU registers 80 | address &= 7; 81 | if ( write ) { 82 | return this.system.ppu.writeRegister( address, value ); 83 | } else { 84 | return this.system.ppu.readRegister( address ); 85 | } 86 | } else { // 0x4000 <= address < 0x4020 87 | // APU registers 88 | address &= 0xff; 89 | if ( write ) { 90 | return this.system.apu.writeRegister( address, value ); 91 | } else { 92 | return this.system.apu.readRegister( address ); 93 | } 94 | } 95 | }, 96 | 97 | read: function( address ) { 98 | return this.readWrite( address, false, 0 ); 99 | }, 100 | 101 | write: function( address, value ) { 102 | return this.readWrite( address, true, value ); 103 | } 104 | }; 105 | 106 | module.exports = Memory; -------------------------------------------------------------------------------- /system/utils/bitmap.js: -------------------------------------------------------------------------------- 1 | var colors = initColors(); 2 | var reversedBytes = initReversedBytes(); 3 | var reversedTiles = initReversedTiles(); 4 | 5 | /** 6 | * Get the colors of a tile. 7 | * Note that a 'tile' is a 16 bit word, with the low colors as the first 8 bits and the high 8 | * colors as the last 8 bits. 9 | */ 10 | exports.getColors = function( tile ) { 11 | var offset = tile << 8; 12 | return colors.subarray( offset, offset + 8 ); 13 | }; 14 | 15 | exports.reverseByte = reverseByte; 16 | 17 | /** 18 | * Reverse all the pixels in a tile. 19 | * Note that a 'tile' is a 16 bit word, with the low colors as the first 8 bits and the high 20 | * colors as the last 8 bits. 21 | */ 22 | exports.reverseTile = function( tile ) { 23 | return reversedTiles[ tile ]; 24 | }; 25 | 26 | /** 27 | * Reverse all the bits in a byte. 28 | */ 29 | function reverseByte( byte ) { 30 | return reversedBytes[ byte ]; 31 | } 32 | 33 | /** 34 | * Initialize lookup table for getColors(). 35 | */ 36 | function initColors() { 37 | var low, high, 38 | result = new Uint8Array( 0x1000000 ); 39 | 40 | for ( low = 0; low < 0x100; low++ ) { 41 | for ( high = 0; high < 0x100; high++ ) { 42 | addTile( low, high ); 43 | } 44 | } 45 | 46 | return result; 47 | 48 | function addTile( low, high ) { 49 | var colorLow, colorHigh, color, 50 | offset = ( low << 16 ) | ( high << 8 ); 51 | 52 | for ( var i = 0; i < 8; i++ ) { 53 | colorLow = ( low & 1 ); 54 | colorHigh = ( high & 1 ) << 1; 55 | color = ( colorHigh | colorLow ); 56 | 57 | result[ offset + i ] = color; 58 | 59 | low >>= 1; 60 | high >>= 1; 61 | } 62 | } 63 | } 64 | 65 | /** 66 | * Initialize lookup table for reverseTile(). 67 | */ 68 | function initReversedTiles() { 69 | var low, high, tile, reversed, 70 | result = new Uint16Array( 0x10000 ); 71 | 72 | for ( low = 0; low < 0x100; low++ ) { 73 | for ( high = 0; high < 0x100; high++ ) { 74 | tile = ( low << 8 ) | high; 75 | reversed = ( reverseByte( low ) << 8 ) | reverseByte( high ); 76 | result[ tile ] = reversed; 77 | } 78 | } 79 | 80 | return result; 81 | } 82 | 83 | /** 84 | * Initialize lookup table for reverseByte(). 85 | */ 86 | function initReversedBytes() { 87 | var i, 88 | result = new Uint8Array( 0x100 ); 89 | 90 | for ( i = 0; i < 0x100; i++ ) { 91 | result[ i ] = calcReverse( i ); 92 | } 93 | 94 | return result; 95 | 96 | function calcReverse( original ) { 97 | var i, 98 | reverse = 0; 99 | 100 | for ( i = 7; i >= 0; i-- ) { 101 | reverse |= ( ( original & 1 ) << i ); 102 | original >>>= 1; 103 | } 104 | 105 | return reverse; 106 | } 107 | } -------------------------------------------------------------------------------- /system/output/audiooutput.js: -------------------------------------------------------------------------------- 1 | function AudioOutput() { 2 | this.bufferIndex = 0; 3 | this.bufferLength = 8192; 4 | this.sampleRate = 44100; // will be overwritten by AudioContext sample rate 5 | this.volume = 1.0; 6 | 7 | this.playing = null; 8 | 9 | this.setEnabled( true ); 10 | } 11 | 12 | AudioOutput.prototype = { 13 | /** 14 | * Write sample to buffer. 15 | */ 16 | writeSample: function( sample ) { 17 | this.bufferData[ this.bufferIndex++ ] = sample; 18 | 19 | if ( this.bufferIndex === this.bufferLength ) { 20 | this.bufferIndex = 0; 21 | 22 | if ( this.playing ) { 23 | this.playing.stop(); 24 | } 25 | 26 | this.bufferSource.buffer = this.buffer; 27 | this.playing = this.bufferSource; 28 | this.playing.start( 0 ); 29 | 30 | this.initBuffer(); 31 | } 32 | }, 33 | 34 | /** 35 | * Enable or disable audio output. 36 | * Note: only actually enabled if audio is supported. 37 | * @param {boolean} enabled - Sets whether enabled or not. 38 | */ 39 | setEnabled: function( enabled ) { 40 | this.enabled = enabled && this.isSupported(); 41 | 42 | if ( this.enabled ) { 43 | this.initHardware(); 44 | } 45 | }, 46 | 47 | /** 48 | * Set volume of audio output. 49 | * @param {number} value - The volume, ranging from 0.0 to 1.0 (inclusive). 50 | */ 51 | setVolume: function( value ) { 52 | this.gainNode.gain.value = value; 53 | this.volume = value; 54 | }, 55 | 56 | /** 57 | * Initialize hardware output. 58 | * Does nothing if audio context is already initialized. 59 | */ 60 | initHardware: function() { 61 | // make sure we don't create too many AudioContexts, because this will throw 62 | // an error 63 | if ( this.context ) { 64 | return; 65 | } 66 | 67 | this.initContext(); 68 | this.initBuffer(); 69 | }, 70 | 71 | /** 72 | * Initialize audio context. 73 | */ 74 | initContext: function() { 75 | this.context = new AudioContext(); 76 | this.sampleRate = this.context.sampleRate; 77 | this.gainNode = this.context.createGain(); 78 | this.gainNode.connect( this.context.destination ); 79 | }, 80 | 81 | /** 82 | * Initialize audio buffer. 83 | */ 84 | initBuffer: function() { 85 | this.buffer = this.context.createBuffer(1, this.bufferLength, this.context.sampleRate); 86 | this.bufferData = this.buffer.getChannelData( 0 ); 87 | 88 | this.bufferSource = this.context.createBufferSource(); 89 | this.bufferSource.connect( this.gainNode ); 90 | }, 91 | 92 | /** 93 | * Check if audio output is supported. 94 | */ 95 | isSupported: function() { 96 | return ( typeof AudioContext !== "undefined" ); 97 | } 98 | }; 99 | 100 | module.exports = AudioOutput; 101 | -------------------------------------------------------------------------------- /system/output/renderer/canvas2d.js: -------------------------------------------------------------------------------- 1 | var palette = require("./palette").data; 2 | 3 | function Canvas2DRenderer( el ) { 4 | this.el = el; 5 | 6 | this.initData(); 7 | this.initPalette(); 8 | } 9 | 10 | Canvas2DRenderer.isSupported = function( el ) { 11 | return !!getContext( el ); 12 | }; 13 | 14 | Canvas2DRenderer.prototype = { 15 | renderFrame: function( output ) { 16 | var bgBuffer = output.bgBuffer, 17 | spriteBuffer = output.spriteBuffer, 18 | prioBuffer = output.prioBuffer, 19 | bgColor = output.bgColor, 20 | 21 | reds = this.reds, 22 | greens = this.greens, 23 | blues = this.blues, 24 | 25 | end = bgBuffer.length; 26 | data = this.data; 27 | 28 | var pixelIndex = 0, 29 | outputIndex = 0, 30 | color = 0; 31 | 32 | for ( ; pixelIndex < end; pixelIndex++ ) { 33 | color = bgBuffer[ pixelIndex ]; 34 | if ( spriteBuffer[ pixelIndex ] && !( prioBuffer[ pixelIndex ] && color ) ) { 35 | color = spriteBuffer[ pixelIndex ]; 36 | } 37 | color = ( color || bgColor ); 38 | 39 | data[ outputIndex++ ] = reds[ color ]; 40 | data[ outputIndex++ ] = greens[ color ]; 41 | data[ outputIndex++ ] = blues[ color ]; 42 | outputIndex++; // skip alpha channel 43 | } 44 | 45 | this.image.data.set( data ); 46 | this.context.putImageData( this.image, 0, 0 ); 47 | }, 48 | 49 | /** 50 | * Initialize the video output. 51 | */ 52 | initData: function() { 53 | this.width = 256; 54 | this.height = 224; 55 | this.data = new Uint8Array( this.width * this.height * 4 ); 56 | 57 | for ( var i = 0; i < this.data.length; i++ ) { 58 | this.data[ i ] = 0xff; 59 | } 60 | 61 | this.context = getContext( this.el ); 62 | this.image = this.context.getImageData( 0, 0, this.width, this.height ); 63 | 64 | this.index = 0; 65 | }, 66 | 67 | /** 68 | * Initialize palette for video output. 69 | */ 70 | initPalette: function() { 71 | var color = 0, 72 | i = 0, 73 | address = 0, 74 | view = palette, 75 | buffer = new ArrayBuffer( 0xc0 ), 76 | splitPalette = new Uint8Array( buffer ); 77 | 78 | // first, re-arrange RGB values in a single array (first reds, then blues, then greens) 79 | for ( color = 0; color < 3; color +=1 ) { 80 | for ( i = 0; i < 192; i += 3 ) { 81 | splitPalette[ address ] = view[ i + color ]; 82 | address += 1; 83 | } 84 | } 85 | 86 | // then, make color values separately available in separate arrays: 87 | this.palette = view; 88 | this.reds = new Uint8Array( buffer, 0, 0x40 ); 89 | this.greens = new Uint8Array( buffer, 0x40, 0x40 ); 90 | this.blues = new Uint8Array( buffer, 0x80, 0x40 ); 91 | } 92 | }; 93 | 94 | function getContext( el ) { 95 | return el.getContext( "2d" ); 96 | } 97 | 98 | module.exports = Canvas2DRenderer; -------------------------------------------------------------------------------- /system/controllers/standardcontroller.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | function StandardController() { 4 | this.data = 0; 5 | this.mask = 0; 6 | this.strobe = 0; 7 | 8 | Object.preventExtensions( this ); 9 | } 10 | 11 | StandardController.prototype = { 12 | /** 13 | * Press a button. 14 | * @param {string} button - The button to press ('a', 'b', 'start', 'select', 'left', 'right', 'up', 'down'). 15 | */ 16 | press: function( button ) { 17 | this._press( getBitMask(button) ); 18 | }, 19 | 20 | /** 21 | * Deress a button. 22 | * @param {string} button - The button to depress ('a', 'b', 'start', 'select', 'left', 'right', 'up', 'down'). 23 | */ 24 | depress: function( button ) { 25 | this._depress( getBitMask(button) ); 26 | }, 27 | 28 | /** 29 | * Press several buttons. 30 | * Note: prevents pressing of 'impossible' combinations on the NES (like left+right). 31 | * @param {number} bitmask - An 8-bit bitmask of buttons to press. 32 | */ 33 | _press: function( bitmask ) { 34 | // prevent input that would be impossible with a standard controller 35 | // (this can cause some seriously weird behavior in some games) 36 | if ( bitmask & 3 ) { 37 | // prevent left + right 38 | this._depress( 3 ); 39 | } else if ( bitmask & 12 ) { 40 | // prevent up + down 41 | this._depress( 12 ); 42 | } 43 | 44 | this.data |= bitmask; 45 | }, 46 | 47 | /** 48 | * Dress several buttons. 49 | * @param {number} bitmask - An 8-bit bitmask of buttons to press. 50 | */ 51 | _depress: function( bitmask ) { 52 | this.data &= ~bitmask; 53 | }, 54 | 55 | /** 56 | * Read controller output. 57 | * The output is returned 1 bit at a time. 58 | */ 59 | read: function() { 60 | if ( !this.mask ) { 61 | // all buttons have been output, always return 1 62 | return 1; 63 | } 64 | 65 | var result = this.data & this.mask; 66 | 67 | if ( !this.strobe ) { 68 | this.mask >>= 1; 69 | } 70 | 71 | return +!!result; 72 | }, 73 | 74 | /** 75 | * Set controller strobe. 76 | * If strobe is high, bit shifter is reset until strobe is low. 77 | * @param {number} value - If truthy strobe is high, otherwise strobe is low. 78 | */ 79 | setStrobe: function( value ) { 80 | if ( value ) { 81 | this.mask = 0x80; 82 | } 83 | this.strobe = value; 84 | } 85 | }; 86 | 87 | /** 88 | * Convert a button string ('a', 'start', etc) to an internal bitmask. 89 | */ 90 | function getBitMask( button ) { 91 | return buttonMap[ button.toLowerCase() ] || 0; 92 | } 93 | 94 | var buttonMap = { 95 | "a": 128, 96 | "b": 64, 97 | "select": 32, 98 | "start": 16, 99 | "up": 8, 100 | "down": 4, 101 | "left": 2, 102 | "right": 1 103 | }; 104 | 105 | module.exports = StandardController; -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # NESNES 2 | 3 | NESNES (New EcmaScript NES) is a pure JS NES emulator. Try the [demo](http://koen.kivits.com/nesnes)! 4 | 5 | [![NESNES screenshot](http://koen.kivits.com/nesnes/screenshot.png)](http://koen.kivits.com/nesnes) 6 | 7 | ## Installation 8 | 9 | NESNES can be installed through npm: 10 | 11 | ``` 12 | npm install nesnes 13 | ``` 14 | 15 | ## Getting started 16 | 17 | NESNES can be used as follows: 18 | 19 | ```js 20 | var NesNes = require("nesnes"); 21 | 22 | var emulator = new NesNes( canvas ); 23 | emulator.load( pathToRom, callback ); 24 | ``` 25 | 26 | These are the parameters being used: 27 | * ``canvas``: a <canvas> DOM object to which the video output will be rendered. Not required (though nothing will be rendered if no canvas is given). 28 | * ``pathToRom``: the path to an INES rom file (most ROMs found on the internet are in this format) 29 | * ``callback``: function to be executed once the ROM has been loaded and initialized. If ``true`` is passed instead of a function the ROM will automatically start playing once it has been loaded. 30 | 31 | ## Build standalone 32 | 33 | If you're running NESNES in the browser and don't want to use Browserify for your page, you can build a standalone version: 34 | 35 | ``` 36 | npm install 37 | make 38 | ``` 39 | 40 | This will create ``nesnes.js`` in ``dist/``, which exposes a global NesNes object when included in your web page. Note that this file also comes packaged with the npm module. 41 | 42 | ## Configuration 43 | 44 | Default configuration (keyboard and gamepad input) can be found in ``config.json``. Input can also be configured programmatically: 45 | 46 | ```js 47 | // configure the first player controller to use the gamepad 48 | emulator.input.configure(0, "gamepad", { 49 | "buttons": { 50 | "0": "b", 51 | "1": "a", 52 | "8": "select", 53 | "9": "start" 54 | }, 55 | "axes": { 56 | "0": "horizontal", // map axis 0 to left/right 57 | "1": "vertical", // map axis 1 to up/down 58 | } 59 | }) 60 | ``` 61 | 62 | See ``config.json`` for an example configuration. 63 | 64 | Note that multiple input types can be enabled for a single controller. For example, a single controller can listen to both the keyboard and a gamepad. 65 | 66 | ## Testing 67 | 68 | NESNES includes a basic test server. You can run it by executing: 69 | 70 | ``` 71 | bin/nesnes-server 72 | ``` 73 | 74 | from a directory containing NES ROMs. This starts up an HTTP server at localhost, serving only a simple page containing a NESNES instance and a ROM selection input to be able to test games. NESNES is recompiled on every page load to make it easy to see how your changes affect the emulator. 75 | 76 | A lot of third party test ROMs are included in the ``test`` directory to debug specific parts of the emulator. 77 | 78 | ## My game doesn't work! 79 | 80 | Please file an issue or send a pull request. :) 81 | -------------------------------------------------------------------------------- /system/input/keyboard.js: -------------------------------------------------------------------------------- 1 | var ControllerHandler = require( "./controllerhandler" ); 2 | 3 | var Keyboard = ControllerHandler.extend({ 4 | initialize: function() { 5 | this.handlers = {}; 6 | }, 7 | 8 | /** 9 | * Load configuration. 10 | * @param {object} config - An mapping of keyboard keys to controller buttons. 11 | */ 12 | _configure: function() { 13 | this.initKeyCodes(); 14 | }, 15 | 16 | /** 17 | * Bind keyboard events. 18 | */ 19 | _enable: function() { 20 | this.bindHandler( "keydown" ); 21 | this.bindHandler( "keyup" ); 22 | }, 23 | 24 | /** 25 | * Unbind keyboard events. 26 | */ 27 | _disable: function() { 28 | this.unbindHandler( "keydown" ); 29 | this.unbindHandler( "keyup" ); 30 | }, 31 | 32 | /** 33 | * Bind keyboard event of specific type. 34 | * @param {string} type - Either 'keydown' or 'keyup'. 35 | */ 36 | bindHandler: function( type ) { 37 | window.addEventListener( type, this.getHandler( type ) ); 38 | }, 39 | 40 | /** 41 | * Unbind keyboard event of specific type. 42 | * @param {string} type - Either 'keydown' or 'keyup'. 43 | */ 44 | unbindHandler: function( type ) { 45 | window.removeEventListener( type, this.getHandler( type ) ); 46 | }, 47 | 48 | /** 49 | * Get keyboard event handler of specific type. 50 | * @param {string} type - Either 'keydown' or 'keyup'. 51 | */ 52 | getHandler: function( type ) { 53 | if ( this.handlers[ type ] ) { 54 | return this.handlers[ type ]; 55 | } 56 | 57 | var self = this, 58 | handler = type === "keydown" ? "press" : "depress"; 59 | 60 | this.handlers[ type ] = function( e ) { 61 | var keyCode = e.keyCode; 62 | 63 | if ( keyCode in self.keyCodes ) { 64 | self[ handler ]( self.keyCodes[ keyCode ] ); 65 | e.preventDefault(); 66 | } 67 | }; 68 | 69 | return this.handlers[ type ]; 70 | }, 71 | 72 | /** 73 | * Initialize keycodes from config. 74 | * Converts config key strings to numeric keycodes that can be used in event handlers. 75 | */ 76 | initKeyCodes: function() { 77 | var name, keyCode, 78 | keyCodes = {}; 79 | 80 | for ( name in this.config ) { 81 | if ( name in keyCodeMap ) { 82 | // special cases ('ctrl', 'shift', etc) 83 | keyCode = keyCodeMap[ name ]; 84 | } else { 85 | // letters and numbers 86 | keyCode = name.toUpperCase().charCodeAt(); 87 | } 88 | 89 | keyCodes[ keyCode ] = this.config[ name ]; 90 | } 91 | 92 | this.keyCodes = keyCodes; 93 | } 94 | }); 95 | 96 | var keyCodeMap = { 97 | "backspace": 8, 98 | "tab": 9, 99 | "return": 13, 100 | "shift": 16, 101 | "ctrl": 17, 102 | "alt": 18, 103 | "capslock": 20, 104 | "space": 32, 105 | "left": 37, 106 | "up": 38, 107 | "right": 39, 108 | "down": 40, 109 | }; 110 | 111 | module.exports = Keyboard; 112 | -------------------------------------------------------------------------------- /test/sprite_overflow/source/ppu_sync.a: -------------------------------------------------------------------------------- 1 | 2 | ; Same as sync_ppu_20 plus next frame is odd (clock subtracted) or even 3 | ; (no clock subtracted). 4 | sync_ppu_odd_20: 5 | lda #$80 6 | bne sync_ppu_frame_ 7 | sync_ppu_even_20: 8 | lda #$00 9 | sync_ppu_frame_: 10 | pha 11 | ; Synchronize with PPU 12 | jsr sync_ppu_20 ; synchronize with PPU 13 | 14 | ; Run for two frames with BG enabled. One of the two frames will 15 | ; be one PPU clock shorter. Note whether the first frame was the 16 | ; shorter one. 17 | 18 | lda #$08 ; 6 enable bg 19 | sta $2001 20 | ldy #41 ; 29785 delay 21 | lda #144 22 | jsr delay_ya2 23 | nop ; 2 delay 24 | pla ; 4 25 | eor $2002 ; 4 find whether frame was odd or even 26 | pha ; 3 27 | ; run another enabled frame so that clock will 28 | ; have been subtracted on one of the two frames 29 | ldy #43 ; 29730+ delay 30 | lda #137 31 | jsr delay_ya1 32 | lda #$00 ; 6 disable bg 33 | sta $2001 34 | 35 | ; If the first frame was shorter, wait three frames to switch 36 | ; the even/odd synchronization without changing the CPU clock's 37 | ; synchronization with the PPU clock. 38 | 39 | pla ; 4 40 | pha ; 3 41 | bpl + ; 3 42 | ; -1 43 | ldy #75 ; 89343 delay 44 | lda #237 45 | jsr delay_ya1 46 | : pla ; 4 47 | rts ; 6 48 | .code 49 | 50 | ; After return, 30 clocks until VBL flag will read 51 | ; as set, then 29781, 29780, 29781, 29781, 29780, etc. 52 | ; Turns off PPU rendering, NMI, IRQ, and DMC. 53 | sync_ppu_align2_30: 54 | pha 55 | txa 56 | pha 57 | tya 58 | pha 59 | lda #0 ; disable dmc and irq 60 | sta $4015 61 | sei 62 | jsr wait_vbl 63 | lda #0 ; disable bg and nmi 64 | sta $2000 65 | sta $2001 66 | 67 | bit $2002 68 | : bit $2002 ; 1 69 | bpl - ; 2 70 | 71 | ldy #141 ; 29774 delay 72 | lda #41 73 | jsr delay_ya6 74 | 75 | : ldy #86 ; 29774 delay 76 | lda #68 77 | jsr delay_ya1 78 | 79 | bit $2002 ; 1 80 | bpl - ; 2 81 | 82 | ldy #28 ; 29726 delay 83 | lda #211 84 | jsr delay_ya1 85 | 86 | pla ; 16 87 | tay 88 | pla 89 | tax 90 | pla 91 | 92 | rts ; 6 93 | .code 94 | 95 | sync_ppu_align1_30: 96 | jsr sync_ppu_align2_30 97 | ldy #86 ; 29775 delay 98 | lda #68 99 | jsr delay_ya2 100 | rts 101 | .code 102 | 103 | sync_ppu_align1_31: 104 | jsr sync_ppu_align2_30 105 | ldy #86 ; 29774 delay 106 | lda #68 107 | jsr delay_ya1 108 | rts 109 | .code 110 | 111 | sync_ppu_align0_30: 112 | jsr sync_ppu_align1_30 113 | ldy #86 ; 29774 delay 114 | lda #68 115 | jsr delay_ya1 116 | rts 117 | .code 118 | 119 | sync_ppu_20: 120 | jsr sync_ppu_align2_30 121 | nop ; 4 122 | nop 123 | rts ; 6 124 | .code 125 | -------------------------------------------------------------------------------- /system/mappers/mmc1.js: -------------------------------------------------------------------------------- 1 | // map MMC1 mirroring modes to INES mirroring values 2 | var mirrorMap = [ 3, 4, 1, 0 ]; 3 | 4 | module.exports = { 5 | init: function() { 6 | this.lastPRG = this.prgBanks - 1; 7 | this.prgBank = 0; 8 | 9 | this.chrBank0 = 0; 10 | this.chrBank1 = 1; 11 | 12 | this.registerWrites = 0; 13 | this.registerShift = 0; 14 | 15 | this.mapperControl( 0xc ); 16 | }, 17 | 18 | mapperControl: function( value ) { 19 | var mirroring = value & 0x3, 20 | prgMode = ( value & 0xc ) >> 2, 21 | chrMode = ( value & 0x10 ) >> 4; 22 | 23 | this.mapperFlags = value; 24 | 25 | this.mirroring = mirrorMap[ mirroring ]; 26 | this.prgMode = prgMode; 27 | this.chrMode = chrMode; 28 | 29 | this.setPRGBanks(); 30 | }, 31 | 32 | setRegister: function( address, value ) { 33 | switch( address & 0x6000 ) { 34 | case 0x0000: 35 | this.mapperControl( value ); 36 | break; 37 | case 0x2000: 38 | this.chrBank0 = value; 39 | this.setChrBanks(); 40 | 41 | break; 42 | case 0x4000: 43 | this.chrBank1 = value; 44 | this.setChrBanks(); 45 | 46 | break; 47 | case 0x6000: 48 | // TODO -- enable/disable RAM on bit 5 49 | value &= 0xf; 50 | 51 | this.prgBank = value; 52 | this.setPRGBanks(); 53 | 54 | break; 55 | } 56 | 57 | this.registerWrites = 0; 58 | this.registerShift = 0; 59 | }, 60 | 61 | setPRGBanks: function() { 62 | var bank0, bank1; 63 | 64 | switch( this.prgMode ) { 65 | case 0: 66 | case 1: 67 | bank0 = this.prgBank & ~1; 68 | bank1 = this.prgBank + 1; 69 | break; 70 | case 2: 71 | bank0 = 0; 72 | bank1 = this.prgBank; 73 | break; 74 | case 3: 75 | bank0 = this.prgBank; 76 | bank1 = this.lastPRG; 77 | break; 78 | } 79 | 80 | this.loadPRGBank( 0x8000, bank0, 0x4000 ); 81 | this.loadPRGBank( 0xc000, bank1, 0x4000 ); 82 | }, 83 | 84 | setChrBanks: function() { 85 | var bank0, bank1; 86 | 87 | if ( !this.chrMode ) { 88 | bank0 = this.chrBank0 & ~1; 89 | bank1 = bank0 + 1; 90 | } else { 91 | bank0 = this.chrBank0; 92 | bank1 = this.chrBank1; 93 | 94 | if ( this.chrBanks < 2 ) { 95 | bank0 &= 1; 96 | bank1 &= 1; 97 | } 98 | } 99 | 100 | this.loadCHRBank( 0, bank0, 0x1000 ); 101 | this.loadCHRBank( 0x1000, bank1, 0x1000 ); 102 | }, 103 | 104 | writeRegister: function( address, value ) { 105 | // TODO ignore consecutive writes 106 | if ( value & 0x80 ) { 107 | // reset mapper register 108 | 109 | this.registerShift = 0; 110 | this.registerWrites = 0; 111 | this.mapperControl( this.mapperFlags | 0xc ); 112 | } else { 113 | // write to register 114 | 115 | this.registerShift = ( this.registerShift >> 1 ) | (( value & 1 ) << 4); 116 | this.registerWrites++; 117 | 118 | if ( this.registerWrites === 5 ) { 119 | this.setRegister( address, this.registerShift ); 120 | } 121 | } 122 | } 123 | }; -------------------------------------------------------------------------------- /test/nestress/TanksPal.ASM: -------------------------------------------------------------------------------- 1 | ; tankspal.bin 2 | 3 | DC.B $0d, $27, $37, $02, $0d, $17, $27, $12, $0d, $07, $17, $22, $0d, $06, $27, $38 4 | DC.B $0d, $0a, $1a, $2a, $2d, $05, $26, $37, $0d, $00, $00, $00, $0d, $00, $00, $00 5 | 6 | DC.B $0d, $06, $10, $1e, $0d, $07, $00, $2e, $0d, $08, $2d, $1d, $0d, $06, $27, $38 7 | DC.B $0d, $0a, $1a, $2a, $2d, $05, $26, $37, $0d, $00, $00, $00, $0d, $00, $00, $00 8 | 9 | DC.B $0d, $16, $26, $03, $0d, $06, $16, $04, $0d, $07, $06, $05, $0d, $06, $27, $38 10 | DC.B $0d, $0a, $1a, $2a, $2d, $05, $26, $37, $0d, $00, $00, $00, $0d, $00, $00, $00 11 | 12 | DC.B $0d, $20, $32, $30, $0d, $28, $2a, $37, $0d, $16, $28, $27, $0d, $06, $27, $38 13 | DC.B $0d, $0a, $1a, $2a, $2d, $05, $26, $37, $0d, $00, $00, $00, $0d, $00, $00, $00 14 | 15 | DC.B $0d, $28, $38, $06, $0d, $17, $28, $30, $0d, $17, $27, $16, $0d, $06, $27, $38 16 | DC.B $0d, $0a, $1a, $2a, $2d, $05, $26, $37, $0d, $00, $00, $00, $0d, $00, $00, $00 17 | 18 | DC.B $0d, $3c, $20, $30, $0d, $3b, $3c, $3c, $0d, $2b, $3b, $2c, $0d, $06, $27, $38 19 | DC.B $0d, $0a, $1a, $2a, $2d, $05, $26, $37, $0d, $00, $00, $00, $0d, $00, $00, $00 20 | 21 | DC.B $0d, $21, $31, $2c, $0d, $11, $21, $1c, $0d, $01, $11, $0c, $0d, $06, $27, $38 22 | DC.B $0d, $0a, $1a, $2a, $2d, $05, $26, $37, $0d, $2c, $3c, $30, $0d, $00, $00, $00 23 | 24 | DC.B $0d, $00, $10, $1e, $0d, $2d, $00, $2f, $0d, $0b, $2d, $1d, $0d, $06, $27, $38 25 | DC.B $0d, $0a, $1a, $2a, $2d, $05, $26, $37, $0d, $00, $00, $00, $0d, $00, $00, $00 26 | 27 | DC.B $0d, $13, $23, $16, $0d, $03, $13, $27, $0d, $01, $03, $38, $0d, $06, $27, $38 28 | DC.B $0d, $0a, $1a, $2a, $0d, $05, $26, $37, $0d, $00, $00, $00, $0d, $00, $00, $00 29 | 30 | DC.B $0d, $17, $27, $16, $0d, $07, $17, $28, $0d, $0c, $1b, $29, $0d, $06, $27, $38 31 | DC.B $0d, $0a, $1a, $2a, $0d, $05, $26, $37, $0d, $00, $00, $00, $0d, $00, $00, $00 32 | 33 | DC.B $0d, $2d, $10, $20, $0d, $0d, $0d, $0d, $0d, $0d, $0d, $0d, $0d, $0d, $0d, $0d 34 | DC.B $0d, $06, $16, $36, $0d, $0a, $2a, $3a, $0d, $00, $10, $20, $0d, $0d, $0d, $0d 35 | 36 | DC.B $0d, $02, $27, $20, $0d, $08, $27, $37, $0d, $11, $21, $31, $0d, $07, $16, $28 37 | DC.B $0d, $1c, $3c, $20, $0d, $08, $00, $10, $0d, $03, $13, $10, $0d, $03, $06, $16 38 | 39 | DC.B $0d, $06, $1a, $38, $0d, $01, $00, $30, $0d, $01, $11, $21, $0d, $21, $31, $30 40 | DC.B $0d, $07, $17, $27, $0d, $16, $26, $37, $0d, $12, $3c, $30, $0d, $06, $27, $38 41 | 42 | DC.B $0d, $10, $10, $10, $0d, $16, $28, $39, $0d, $2d, $10, $30, $0d, $1d, $01, $11 43 | DC.B $0d, $1d, $28, $17, $0d, $1c, $3c, $20, $0d, $19, $30, $30, $0d, $0d, $0d, $0d 44 | 45 | DC.B $0d, $06, $16, $26, $0d, $07, $17, $27, $0d, $08, $18, $28, $0d, $09, $19, $29 46 | DC.B $0d, $0d, $0d, $0d, $0d, $0d, $0d, $0d, $0d, $0d, $0d, $0d, $0d, $0d, $0d, $0d 47 | -------------------------------------------------------------------------------- /system/mappers/mmc2.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | init: function() { 3 | this.setPrgBank( 0 ); 4 | 5 | this.mmc2PRG = this.prgBanks << 1; 6 | this.loadPRGBank( 0xa000, this.mmc2PRG - 3, 0x2000 ); 7 | this.loadPRGBank( 0xc000, this.mmc2PRG - 2, 0x2000 ); 8 | this.loadPRGBank( 0xe000, this.mmc2PRG - 1, 0x2000 ); 9 | 10 | this.chrLatch0 = false; 11 | this.chrLatch1 = false; 12 | 13 | this.chrBank0 = this.chrBank1 = 0; 14 | this.chrBank2 = this.chrBank3 = 0; 15 | 16 | this.chrBankA = this.chrBank0; 17 | this.chrBankB = this.chrBank1; 18 | 19 | this.setChrBanks(); 20 | 21 | this.initLatchSwitches(); 22 | 23 | this._readCHR = Object.getPrototypeOf( this ).readCHR; 24 | }, 25 | 26 | initLatchSwitches: function() { 27 | var i, 28 | switches = new Uint8Array( 0x2000 ); 29 | switches[ 0xfd8 ] = 1; 30 | switches[ 0xfe8 ] = 2; 31 | 32 | for ( i = 0x1fd8; i < 0x1fe0; i++ ) { 33 | switches[ i ] = 3; 34 | } 35 | for ( i = 0x1fe8; i < 0x1ff0; i++ ) { 36 | switches[ i ] = 4; 37 | } 38 | 39 | this.latchSwitches = switches; 40 | }, 41 | 42 | setChrBanks: function() { 43 | var bank0 = ( this.chrLatch0 ? this.chrBank1 : this.chrBank0 ), 44 | bank1 = ( this.chrLatch1 ? this.chrBank3 : this.chrBank2 ); 45 | 46 | this.loadCHRBank( 0x0000, bank0, 0x1000 ); 47 | this.loadCHRBank( 0x1000, bank1, 0x1000 ); 48 | }, 49 | 50 | setPrgBank: function( bank ) { 51 | this.prgBank = bank; 52 | this.loadPRGBank( 0x8000, this.prgBank, 0x2000 ); 53 | }, 54 | 55 | writeRegister: function( address, value ) { 56 | switch ( address & 0x7000 ) { 57 | case 0x2000: 58 | // $a000 - $afff 59 | this.setPrgBank( value & 0xf ); 60 | break; 61 | case 0x3000: 62 | // $b000 - $bfff 63 | this.chrBank0 = value & 0x1f; 64 | break; 65 | case 0x4000: 66 | // $c000 - $cfff 67 | this.chrBank1 = value & 0x1f; 68 | break; 69 | case 0x5000: 70 | // $d000 - $dfff 71 | this.chrBank2 = value & 0x1f; 72 | break; 73 | case 0x6000: 74 | // $e000 - $efff 75 | this.chrBank3 = value & 0x1f; 76 | break; 77 | case 0x7000: 78 | // $f000 - $ffff 79 | this.mirroring = +!( value & 1 ); 80 | break; 81 | } 82 | 83 | this.setChrBanks(); 84 | }, 85 | 86 | readCHR: function( address ) { 87 | var value = this._readCHR( address ); 88 | 89 | switch ( this.latchSwitches[address] ) { 90 | case 0: 91 | break; 92 | case 1: 93 | if ( this.chrLatch0 ) { 94 | this.chrLatch0 = false; 95 | this.setChrBanks(); 96 | } 97 | break; 98 | case 2: 99 | if ( !this.chrLatch0 ) { 100 | this.chrLatch0 = true; 101 | this.setChrBanks(); 102 | } 103 | break; 104 | case 3: 105 | if ( this.chrLatch1 ) { 106 | this.chrLatch1 = false; 107 | this.setChrBanks(); 108 | } 109 | break; 110 | case 4: 111 | if ( !this.chrLatch1 ) { 112 | this.chrLatch1 = true; 113 | this.setChrBanks(); 114 | } 115 | break; 116 | } 117 | 118 | return value; 119 | } 120 | }; -------------------------------------------------------------------------------- /test/sprite_overflow/source/2.Details.a: -------------------------------------------------------------------------------- 1 | ; Tests details of sprite overflow flag 2 | 3 | .include "prefix.a" 4 | 5 | test_name: 6 | .db "SPRITE OVERFLOW DETAILS",0 7 | .code 8 | 9 | reset: 10 | jsr begin_sprite_overflow_tests 11 | 12 | ; Move first 9 sprites to Y = 128, X = 0 13 | jsr clear_sprite_table 14 | lda #128 15 | ldx #0 16 | ldy #9 17 | jsr move_sprites 18 | lda #0 19 | ldx #3 20 | ldy #9 21 | jsr move_sprites 22 | lda #2;) Should be set even when sprites are under left clip (X = 0) 23 | ldx #$18 24 | jsr sprites_should_overflow 25 | 26 | lda #3;) Disabling rendering shouldn't clear flag 27 | ldx #$18 28 | jsr test_for_overflow 29 | lda #0 30 | sta $2001 31 | lda $2002 ; raw scanline 10 of next frame 32 | and #$20 33 | jsr error_if_eq 34 | lda #4;) Should be cleared at the end of VBL even when rendering is off 35 | ldy #20 36 | jsr delay_y_scanlines 37 | lda $2002 ; raw scanline 30 of next frame 38 | and #$20 39 | jsr error_if_ne 40 | 41 | jsr clear_sprite_table 42 | lda #239 43 | ldx #0 44 | ldy #9 45 | jsr move_sprites 46 | lda #5;) Should be set when sprite Y coordinates are 239 47 | ldx #$18 48 | jsr sprites_should_overflow 49 | 50 | jsr clear_sprite_table 51 | lda #240 52 | ldx #0 53 | ldy #64 54 | jsr move_sprites 55 | lda #6;) Shouldn't be set when sprite Y coordinates are 240 (off screen) 56 | ldx #$18 57 | jsr sprites_should_not_overflow 58 | 59 | jsr clear_sprite_table 60 | lda #255 61 | ldx #0 62 | ldy #64 63 | jsr move_sprites 64 | lda #7;) Shouldn't be set when sprite Y coordinates are 255 (off screen) 65 | ldx #$18 66 | jsr sprites_should_not_overflow 67 | 68 | jsr clear_sprite_table 69 | ldx #0 70 | ldy #11 71 | lda #128 72 | jsr move_sprites 73 | lda #240 74 | sta sprites + 0 75 | sta sprites + 8 76 | lda #8;) Should be set regardless of which sprites are involved 77 | ldx #$18 78 | jsr sprites_should_overflow 79 | 80 | ldy #0 81 | lda #0 82 | : sta sprites,y 83 | clc 84 | adc #1 85 | iny 86 | iny 87 | iny 88 | iny 89 | bne - 90 | lda #9;) Shouldn't be set when all scanlines have 7 or fewer sprites 91 | ldx #$18 92 | jsr sprites_should_not_overflow 93 | 94 | jsr clear_sprite_table 95 | ldx #8 96 | ldy #7 97 | lda #128 98 | jsr move_sprites 99 | lda #113 100 | sta sprites + 0 101 | sta sprites + 4 102 | lda #10;) Double-height sprites aren't handled properly 103 | ldy #$20 104 | sty $2000 105 | ldx #$18 106 | jsr sprites_should_overflow 107 | 108 | jmp tests_passed 109 | -------------------------------------------------------------------------------- /system/input/controllerhandler.js: -------------------------------------------------------------------------------- 1 | function ControllerHandler( controller, config ) { 2 | this.controller = controller; 3 | 4 | if ( config ) { 5 | this.configure( config ); 6 | } 7 | this.initialize(); 8 | this.enabled = false; 9 | 10 | this._pressed = {}; 11 | } 12 | 13 | ControllerHandler.prototype = { 14 | /** 15 | * Initialize handler. 16 | * Will be called from constructor, should be overwritten by subclass. 17 | */ 18 | initialize: function() { 19 | }, 20 | 21 | /** 22 | * Load configuration. 23 | * @param {object} config 24 | */ 25 | configure: function( config ) { 26 | this.config = config; 27 | this._configure(); 28 | }, 29 | 30 | /** 31 | * Configuration handler. 32 | * Called from configure(), should be overwritten by subclass. 33 | */ 34 | _configure: function() {}, 35 | 36 | /** 37 | * Bind keyboard events. 38 | */ 39 | enable: function() { 40 | // make sure event handlers aren't bound twice 41 | if ( this.isSupported() && !this.enabled ) { 42 | this.enabled = true; 43 | this._enable(); 44 | } 45 | }, 46 | 47 | /** 48 | * Enable handler. 49 | * Called from enable(), should be overwritten by subclass. 50 | */ 51 | _enable: function() {}, 52 | 53 | /** 54 | * Unbind keyboard events. 55 | */ 56 | disable: function() { 57 | if ( this.enabled ) { 58 | this.enabled = false; 59 | this._disable(); 60 | } 61 | }, 62 | 63 | /** 64 | * Disable handler. 65 | * Called from disable(), should be overwritten by subclass. 66 | */ 67 | _disable: function() {}, 68 | 69 | /** 70 | * Check if controller hander is supported. 71 | * Should be overwitten by subclass. 72 | */ 73 | isSupported: function() { 74 | return true; 75 | }, 76 | 77 | /** 78 | * Proxy controller press calls. 79 | * Keeps track of which buttons are pressed by this handler. 80 | */ 81 | press: function( button ) { 82 | this._pressed[ button ] = true; 83 | this.controller.press( button ); 84 | }, 85 | 86 | /** 87 | * Proxy controller depress calls. 88 | * Only allows depresses of buttons pressed by this handler. This allows for 89 | * multiple handlers to control the same controller. 90 | */ 91 | depress: function( button ) { 92 | if ( button in this._pressed ) { 93 | delete this._pressed[ button ]; 94 | this.controller.depress( button ); 95 | } 96 | } 97 | }; 98 | 99 | /** 100 | * Quick class 'extend' method. 101 | * Not all target browsers support ES6, I can't precompile because of 102 | * performance issues, and I only need it here (so I won't drag in an 103 | * extra dependency). 104 | */ 105 | ControllerHandler.extend = function( proto ) { 106 | Handler = function( controller, config ) { 107 | ControllerHandler.call( this, controller, config ); 108 | }; 109 | 110 | Handler.prototype = Object.create( ControllerHandler.prototype ); 111 | 112 | for ( var key in proto ) { 113 | Handler.prototype[ key ] = proto[ key ]; 114 | } 115 | 116 | return Handler; 117 | }; 118 | 119 | module.exports = ControllerHandler; 120 | -------------------------------------------------------------------------------- /system/apu/dmc.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | var Channel = require("./channel"); 4 | 5 | function DMC( apu ) { 6 | this.apu = apu; 7 | 8 | this.timerMax = periodLookup[ 0 ]; 9 | this.timer = this.period; 10 | 11 | this.silence = false; 12 | this.output = 0; 13 | 14 | this.sampleAddress = 0; 15 | this.sampleCurrentAddress = 0; 16 | this.sampleLength = 0; 17 | this.sampleBytesLeft = 0; 18 | this.sampleBuffer = 0; 19 | this.loop = false; 20 | this.interrupt = false; 21 | 22 | this.bitsLeft = 8; 23 | this.shifter = 0; 24 | 25 | this.irqEnabled = false; 26 | 27 | Channel.call( this ); 28 | Object.preventExtensions( this ); 29 | } 30 | 31 | DMC.prototype = new Channel(); 32 | 33 | DMC.prototype.writeRegister = function( index, value ) { 34 | switch ( index ) { 35 | case 0: 36 | this.irqEnabled = !!( value & 0x80 ); 37 | if ( !this.irqEnabled ) { 38 | this.interrupt = false; 39 | } 40 | 41 | this.loop = !!( value & 0x40 ); 42 | this.timerMax = this.timer = periodLookup[ value & 0xf ] >>> 1; 43 | 44 | break; 45 | case 1: 46 | this.output = value & 0x7f; 47 | break; 48 | case 2: 49 | this.sampleAddress = this.sampleCurrentAddress = ( 0xc000 | ( value << 6 ) ); 50 | break; 51 | case 3: 52 | this.sampleLength = this.sampleBytesLeft = value && (( value << 4 ) | 1); 53 | break; 54 | } 55 | }; 56 | 57 | DMC.prototype.doTimer = function() { 58 | if ( this.timerMax ) { 59 | this.timer--; 60 | 61 | if ( this.timer <= 0 ) { 62 | // output bit of shift register 63 | if ( !this.silence ) { 64 | // TODO: should not do inc/dec if limit would be exceeded 65 | if ( this.shifter & 1 ) { 66 | this.output = Math.min( this.output + 2, 127 ); 67 | } else { 68 | this.output = Math.max( this.output - 2, 0 ); 69 | } 70 | 71 | this.sample = this.output; 72 | } else { 73 | this.sample = 0; 74 | } 75 | 76 | // clock shift register 77 | this.shifter >>>= 1; 78 | 79 | // decrement bits left counter, possibly ending output cycle 80 | this.bitsLeft--; 81 | 82 | if ( !this.bitsLeft ) { 83 | this.bitsLeft = 8; 84 | 85 | this.silence = !this.sampleBytesLeft; 86 | if ( !this.silence ) { 87 | this.readSample(); 88 | } 89 | } 90 | 91 | this.timer += this.timerMax; 92 | } 93 | } 94 | 95 | if ( this.irqEnabled && this.interrupt ) { 96 | this.apu.system.cpu.requestIRQ(); 97 | } 98 | }; 99 | 100 | DMC.prototype.readSample = function() { 101 | this.shifter = this.sampleBuffer; 102 | 103 | // TODO stall CPU 104 | this.sampleBuffer = this.apu.system.memory.read( this.sampleCurrentAddress ); 105 | 106 | this.sampleCurrentAddress++; 107 | if ( this.sampleCurrentAddress === 0xffff ) { 108 | this.sampleCurrentAddress = 0x8000; 109 | } 110 | 111 | this.sampleBytesLeft--; 112 | if ( !this.sampleBytesLeft ) { 113 | this.sampleCurrentAddress = this.sampleAddress; 114 | 115 | if ( this.loop ) { 116 | this.sampleBytesLeft = this.sampleLength; 117 | } else { 118 | this.interrupt = this.irqEnabled; 119 | } 120 | } 121 | }; 122 | 123 | // TODO PAL 124 | var periodLookup = [ 125 | 428, 380, 340, 320, 286, 254, 226, 214, 190, 160, 142, 128, 106, 84, 72, 54 126 | ]; 127 | 128 | module.exports = DMC; -------------------------------------------------------------------------------- /system/apu/pulse.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | var Channel = require("./channel"); 4 | 5 | function Pulse() { 6 | this.timerMax = 0; 7 | this.timer = 0; 8 | 9 | this.duty = 0; 10 | 11 | this.sweepStart = true; 12 | this.sweepEnabled = false; 13 | this.sweepDividerPeriod = 0; 14 | this.sweepDividerCount = 0; 15 | this.sweepNegate = false; 16 | this.sweepShiftCount = 0; 17 | 18 | this.pulseCounter = 0; 19 | 20 | this.silence = false; 21 | 22 | Channel.call( this ); 23 | Object.preventExtensions( this ); 24 | } 25 | 26 | Pulse.prototype = new Channel(); 27 | 28 | Pulse.prototype.doSweep = function() { 29 | var adjustPulse = !this.sweepDividerCount, 30 | timerDelta = 0, 31 | targetTimer = 0; 32 | 33 | if ( this.sweepStart ) { 34 | this.sweepDividerCount = this.sweepDividerPeriod; 35 | this.sweepStart = false; 36 | } 37 | 38 | if ( adjustPulse ) { 39 | if ( this.sweepShiftCount ) { 40 | timerDelta = this.timerMax >>> this.sweepShiftCount; 41 | // TODO broken sweep, see last line on http://wiki.nesdev.com/w/index.php/APU_Sweep 42 | } 43 | 44 | if ( this.sweepNegate ) { 45 | timerDelta = -timerDelta; 46 | } 47 | 48 | targetTimer = this.timerMax + timerDelta; 49 | 50 | if ( 51 | this.timerMax >= 8 && 52 | this.timerMax <= 0x7ff 53 | ) { 54 | if ( this.sweepEnabled ) { 55 | this.timerMax = targetTimer; 56 | } 57 | this.silence = false; 58 | } else { 59 | this.silence = true; 60 | } 61 | 62 | this.sweepDividerCount = this.sweepDividerPeriod; 63 | } else if ( this.sweepDividerCount ) { 64 | this.sweepDividerCount--; 65 | } 66 | }; 67 | 68 | Pulse.prototype.doTimer = function() { 69 | if ( !this.silence && this.lengthCounter && this.timerMax) { 70 | if ( this.timer ) { 71 | this.timer--; 72 | } else { 73 | this.timer += this.timerMax; 74 | this.pulseCounter = ( this.pulseCounter + 1 ) & 7; 75 | this.sample = pulseDutyLookup[ ( this.duty << 3 ) + this.pulseCounter ] * this.masterVolume; 76 | } 77 | } else { 78 | this.sample = 0; 79 | } 80 | }; 81 | 82 | Pulse.prototype.writeRegister = function( index, value ) { 83 | switch( index ) { 84 | case 0: 85 | this.duty = ( value & 0xc0 ) >> 6; 86 | this.setEnvelope( value ); 87 | break; 88 | case 1: 89 | this.sweepStart = true; 90 | this.sweepEnabled = !!( value & 0x80 ); 91 | this.sweepDividerPeriod = ( ( value & 0x70 ) >> 4 ) + 1; 92 | this.sweepNegate = !!( value & 0x8 ); 93 | this.sweepShiftCount = value & 0x7; 94 | 95 | break; 96 | case 2: 97 | this.timer = this.timerMax = ( this.timerMax & ~0xff ) | value; 98 | break; 99 | case 3: 100 | // set timer high and length counter 101 | this.timer = this.timerMax = ( this.timerMax & ~0xff00 ) | ( (value & 0x7) << 8); 102 | this.setLengthCounter( value >>> 3 ); 103 | 104 | // restart envelope and sequencer 105 | this.envelopeStart = true; 106 | this.pulseCounter = 0; 107 | 108 | break; 109 | } 110 | 111 | this.silence = ( this.timerMax < 8 ); 112 | }; 113 | 114 | var pulseDutyLookup = [ 115 | 0, 1, 0, 0, 0, 0, 0, 0, // duty 0 116 | 0, 1, 1, 0, 0, 0, 0, 0, // duty 1 117 | 0, 1, 1, 1, 1, 0, 0, 0, // duty 2 118 | 1, 0, 0, 1, 1, 1, 1, 1 // duty 3 119 | ]; 120 | 121 | module.exports = Pulse; -------------------------------------------------------------------------------- /test/ppu/source/04-nmi_control.s: -------------------------------------------------------------------------------- 1 | ; Tests immediate NMI behavior when enabling while VBL flag is already set 2 | 3 | CUSTOM_NMI=1 4 | .include "shell.inc" 5 | 6 | zp_byte nmi_count 7 | 8 | nmi: inc nmi_count 9 | rti 10 | 11 | ; Waits until NMI is about to occur 12 | begin: lda #0 13 | sta $2000 14 | jsr wait_vbl 15 | delay 29600 16 | lda #0 17 | sta nmi_count 18 | rts 19 | 20 | ; Enables NMI, waits, then reads NMI count 21 | end: lda #$80 22 | sta $2000 23 | delay 200 24 | lda nmi_count 25 | rts 26 | 27 | main: set_test 2,"Shouldn't occur when disabled" 28 | jsr begin 29 | delay 200 30 | lda nmi_count 31 | jne test_failed 32 | 33 | set_test 3,"Should occur when enabled and VBL begins" 34 | jsr begin 35 | jsr end 36 | jeq test_failed 37 | 38 | set_test 4,"$2000 should be mirrored every 8 bytes" 39 | jsr begin 40 | lda #$80 41 | sta $2FF8 42 | delay 200 43 | lda nmi_count 44 | jeq test_failed 45 | 46 | set_test 5,"Should occur immediately if enabled while VBL flag is set" 47 | jsr begin 48 | delay 200 ; VBL flag set during this time 49 | jsr end ; NMI is enabled here, and should occur immediately 50 | cmp #1 51 | jne test_failed 52 | 53 | set_test 6,"Shouldn't occur if enabled while VBL flag is clear" 54 | jsr begin 55 | delay 200 56 | lda $2002 ; clear VBL flag 57 | jsr end 58 | jne test_failed 59 | 60 | set_test 7,"Shouldn't occur again if writing $80 when already enabled" 61 | jsr begin 62 | lda #$80 63 | sta $2000 64 | delay 200 ; NMI occurs here 65 | jsr end ; writes $80 again, shouldn't occur again 66 | cmp #1 ; only 1 NMI should have occurred 67 | jne test_failed 68 | 69 | set_test 8,"Shouldn't occur again if writing $80 when already enabled 2" 70 | jsr begin 71 | delay 200 ; VBL flag set during this time 72 | lda #$80 ; enable NMI, which should result in immediate NMI 73 | sta $2000 74 | jsr end ; writes $80 again, shouldn't occur again 75 | cmp #1 ; only 1 NMI should have occurred 76 | jne test_failed 77 | 78 | set_test 9,"Should occur again if enabling after disabled" 79 | jsr begin 80 | lda #$80 81 | sta $2000 82 | delay 200 ; NMI occurs here 83 | lda #$00 ; disable NMI 84 | sta $2000 85 | jsr end ; NMI is enabled again, and should occur immediately 86 | cmp #2 ; 2 NMIs should have occurred 87 | jne test_failed 88 | 89 | set_test 10,"Should occur again if enabling after disabled 2" 90 | jsr begin 91 | delay 200 ; VBL flag set during this time 92 | lda #$80 ; enable NMI, which should result in immediate NMI 93 | sta $2000 94 | lda #$00 ; disable NMI 95 | sta $2000 96 | jsr end ; NMI is enabled again, and should occur immediately 97 | cmp #2 ; 2 NMIs should have occurred 98 | jne test_failed 99 | 100 | set_test 11,"Immediate occurence should be after NEXT instruction" 101 | jsr begin 102 | delay 200 ; VBL flag set during this time 103 | ldx #0 104 | lda #$80 ; enable NMI, which should result in immediate NMI 105 | sta $2000 ; after NEXT instruction 106 | stx nmi_count ; clear nmi_count 107 | ; NMI should occur here 108 | lda nmi_count 109 | jeq test_failed 110 | 111 | jmp tests_passed 112 | -------------------------------------------------------------------------------- /test/ppu2/source/sprite_ram.asm: -------------------------------------------------------------------------------- 1 | ; Tests sprite RAM access via $2003, $2004, and $4014 2 | 3 | .include "prefix_ppu.a" 4 | 5 | sprites = $200 6 | 7 | reset: 8 | lda #50 9 | jsr delay_msec 10 | 11 | jsr wait_vbl 12 | lda #0 13 | sta $2000 14 | sta $2001 15 | 16 | lda #2;) Basic read/write doesn't work 17 | sta result 18 | lda #0 19 | sta $2003 20 | lda #$12 21 | sta $2004 22 | lda #0 23 | sta $2003 24 | lda $2004 25 | cmp #$12 26 | jsr error_if_ne 27 | 28 | lda #3;) Address should increment on $2004 write 29 | sta result 30 | lda #0 31 | sta $2003 32 | lda #$12 33 | sta $2004 34 | lda #$34 35 | sta $2004 36 | lda #1 37 | sta $2003 38 | lda $2004 39 | cmp #$34 40 | jsr error_if_ne 41 | 42 | lda #4;) Address should not increment on $2004 read 43 | sta result 44 | lda #0 45 | sta $2003 46 | lda #$12 47 | sta $2004 48 | lda #$34 49 | sta $2004 50 | lda #0 51 | sta $2003 52 | lda $2004 53 | lda $2004 54 | cmp #$34 55 | jsr error_if_eq 56 | 57 | lda #5;) Third sprite bytes should be masked with $e3 on read 58 | sta result 59 | lda #3 60 | sta $2003 61 | lda #$ff 62 | sta $2004 63 | lda #3 64 | sta $2003 65 | lda $2004 66 | cmp #$e3 67 | jsr error_if_eq 68 | 69 | lda #6;) $4014 DMA copy doesn't work at all 70 | sta result 71 | ldx #0 ; set up data to copy from 72 | : lda test_data,x 73 | sta sprites,x 74 | inx 75 | cpx #4 76 | bne - 77 | lda #0 ; dma copy 78 | sta $2003 79 | lda #$02 80 | sta $4014 81 | ldx #0 ; set up data to copy from 82 | : stx $2003 83 | lda $2004 84 | cmp test_data,x 85 | jsr error_if_ne 86 | inx 87 | cpx #4 88 | bne - 89 | 90 | lda #7;) $4014 DMA copy should start at value in $2003 and wrap 91 | sta result 92 | ldx #0 ; set up data to copy from 93 | : lda test_data,x 94 | sta sprites,x 95 | inx 96 | cpx #4 97 | bne - 98 | lda #1 ; dma copy 99 | sta $2003 100 | lda #$02 101 | sta $4014 102 | ldx #1 ; set up data to copy from 103 | : stx $2003 104 | lda $2004 105 | cmp sprites - 1,x 106 | jsr error_if_ne 107 | inx 108 | cpx #5 109 | bne - 110 | 111 | lda #8;) $4014 DMA copy should leave value in $2003 intact 112 | sta result 113 | lda #1 ; dma copy 114 | sta $2003 115 | lda #$02 116 | sta $4014 117 | lda #$ff 118 | sta $2004 119 | lda #1 120 | sta $2003 121 | lda $2004 122 | cmp #$ff 123 | jsr error_if_ne 124 | 125 | lda #1;) Tests passed 126 | sta result 127 | jmp report_final_result 128 | 129 | test_data: 130 | .db $12,$82,$e3,$78 131 | -------------------------------------------------------------------------------- /test/sprite_overflow/source/5.Emulator.a: -------------------------------------------------------------------------------- 1 | ; Tests things that an optimized emulator is likely get wrong 2 | 3 | .include "prefix.a" 4 | 5 | test_name: 6 | .db "SPRITE OVERFLOW EMULATION",0 7 | .code 8 | 9 | reset: 10 | jsr begin_sprite_overflow_tests 11 | 12 | jsr clear_sprite_table 13 | lda #128 14 | ldx #0 15 | ldy #9 16 | jsr move_sprites 17 | lda #2;) Didn't calculate overflow when no $2002 read for frame 18 | ldx #$18 19 | jsr begin_overflow_test 20 | ldy #16 21 | jsr delay_y_scanlines 22 | ldy #240 23 | jsr delay_y_scanlines 24 | ldy #10 25 | jsr delay_y_scanlines 26 | lda $2002 27 | and #$20 28 | jsr error_if_eq 29 | 30 | lda #3;) Disabling rendering didn't recalculate flag time 31 | sta result 32 | jsr clear_sprite_table 33 | lda #127 ; 9 sprites at 127, 9 at 230 34 | ldx #0 35 | ldy #9 36 | jsr move_sprites 37 | lda #230 38 | ldy #9 39 | jsr move_sprites 40 | jsr wait_vbl 41 | lda #$18 ; enable rendering 42 | sta $2001 43 | jsr dma_sprite_table ; 4.5 scanlines 44 | ldy #21 45 | jsr delay_y_scanlines 46 | ldy #121 47 | jsr delay_y_scanlines 48 | lda $2002 ; have emulator think it'll occur next 49 | lda #$00 ; disable rendering 50 | sta $2001 51 | ldy #12 52 | jsr delay_y_scanlines 53 | lda #$18 ; enable rendering 54 | sta $2001 55 | ldy #92 56 | jsr delay_y_scanlines 57 | lda $2002 58 | pha 59 | ldy #4 60 | jsr delay_y_scanlines 61 | ldx $2002 62 | pla 63 | and #$20 64 | jsr error_if_ne 65 | txa 66 | and #$20 67 | jsr error_if_eq 68 | 69 | lda #4;) Changing sprite RAM didn't recalculate flag time 70 | sta result 71 | lda #248 72 | ldx #0 73 | ldy #2 74 | jsr move_sprites 75 | jsr wait_vbl 76 | lda #$18 ; enable rendering 77 | sta $2001 78 | ldy #142 79 | jsr delay_y_scanlines 80 | lda $2002 ; have emulator think it'll occur next 81 | lda #$00 ; disable rendering 82 | sta $2001 83 | jsr dma_sprite_table 84 | lda #$18 ; enable rendering 85 | sta $2001 86 | ldy #104 87 | jsr delay_y_scanlines 88 | lda $2002 89 | pha 90 | ldy #4 91 | jsr delay_y_scanlines 92 | ldx $2002 93 | pla 94 | and #$20 95 | jsr error_if_ne 96 | txa 97 | and #$20 98 | jsr error_if_eq 99 | 100 | jsr clear_sprite_table 101 | lda #100 102 | ldx #0 103 | ldy #7 104 | jsr move_sprites 105 | lda #115 106 | ldy #2 107 | jsr move_sprites 108 | lda #200 109 | ldy #9 110 | jsr move_sprites 111 | lda #5;) Changing sprite height didn't recalculate time 112 | ldx #$18 113 | jsr begin_overflow_test 114 | ldy #66 115 | jsr delay_y_scanlines 116 | lda $2002 ; have emulator think it'll occur at 200 117 | lda #$20 ; change sprite height so it'll occur at 115 118 | sta $2000 119 | ldy #100 120 | jsr delay_y_scanlines 121 | lda $2002 122 | and #$20 123 | jsr error_if_eq 124 | 125 | jmp tests_passed 126 | 127 | -------------------------------------------------------------------------------- /system/input/index.js: -------------------------------------------------------------------------------- 1 | var Gamepad = require("./gamepad"); 2 | var Keyboard = require("./keyboard"); 3 | var StandardController = require("../controllers/standardcontroller"); 4 | 5 | function Input( system ) { 6 | this.system = system; 7 | this.controllers = system.controllers; 8 | this.inputHandlers = new Array( 2 ); 9 | 10 | this.initConfig(); 11 | 12 | // only enable input in browsers 13 | if ( typeof window !== "undefined" ) { 14 | this.enable(); 15 | } 16 | } 17 | 18 | Input.prototype = { 19 | /** 20 | * Enable all input. 21 | */ 22 | enable: function() { 23 | this._setEnabled( true ); 24 | }, 25 | 26 | /** 27 | * Disable all input. 28 | */ 29 | disable: function() { 30 | this._setEnabled( false ); 31 | }, 32 | 33 | /** 34 | * Set enabled yes/no. 35 | * Helper method for enable and disable. 36 | * @param {boolean} enabled - If true enable, otherwise disable. 37 | */ 38 | _setEnabled: function( enabled ) { 39 | var handlers, type, 40 | method = enabled ? "enable" : "disable"; 41 | 42 | this._enabled = enabled; 43 | for ( var i = 0; i < this.inputHandlers.length; i++ ) { 44 | handlers = this.inputHandlers[ i ]; 45 | if ( handlers ) { 46 | for ( type in handlers ) { 47 | handlers[ type ][ method ](); 48 | } 49 | } 50 | } 51 | }, 52 | 53 | /** 54 | * Initialize total input config. 55 | */ 56 | initConfig: function() { 57 | var i, j, item, controls, 58 | config = this.config = this.system.config.input; 59 | 60 | for ( i = 0; i < config.length; i++ ) { 61 | item = config[ i ]; 62 | 63 | this.setController( i, item.type ); 64 | 65 | controls = item.controls; 66 | if ( !Array.isArray(controls) ) { 67 | controls = [ controls ]; 68 | } 69 | 70 | for ( j = 0; j < controls.length; j++ ) { 71 | this.configure( i, controls[ j ].type, controls[ j ].config ); 72 | } 73 | } 74 | }, 75 | 76 | /** 77 | * Connect a controller of given type. 78 | * @param {number} index - Either 0 or 1. 79 | * @param {string} type - Type of controller (eg. 'standard'). 80 | */ 81 | setController: function( index, type ) { 82 | var Controller = controllerMap[ type ]; 83 | this.controllers.connect( index, new Controller() ); 84 | }, 85 | 86 | /** 87 | * Configure the input for a controller 88 | * @param {number} index - Either 0 or 1 89 | * @param {string} type - Type of input handler (either 'keyboard' or 'gamepad') 90 | * @param {object} config - Configuration for input handler (see config.json for examples) 91 | */ 92 | configure: function( index, type, config ) { 93 | var currentHandler, 94 | InputHandler = inputHandlerMap[ type ], 95 | controller = this.controllers.get( index ); 96 | 97 | this.initInputHandlers( index ); 98 | currentHandler = this.inputHandlers[ index ][ type ]; 99 | if ( currentHandler ) { 100 | currentHandler.disable(); 101 | } 102 | 103 | this.inputHandlers[ index ][ type ] = new InputHandler( controller, config ); 104 | 105 | if ( this._enabled ) { 106 | this.inputHandlers[ index ][ type ].enable(); 107 | } 108 | }, 109 | 110 | initInputHandlers: function( index ) { 111 | this.inputHandlers[ index ] = this.inputHandlers[ index ] || {}; 112 | } 113 | }; 114 | 115 | var controllerMap = { 116 | "standard": StandardController 117 | }; 118 | 119 | var inputHandlerMap = { 120 | "gamepad": Gamepad, 121 | "keyboard": Keyboard 122 | }; 123 | 124 | module.exports = Input; 125 | -------------------------------------------------------------------------------- /test/sprite_overflow/source/prefix_ppu.a: -------------------------------------------------------------------------------- 1 | .include "validation.a" 2 | .include "ppu_sync.a" 3 | 4 | .default palette = default_palette 5 | .default tiles = default_tiles 6 | 7 | begin_ppu_test: 8 | lda #0 9 | sta result 10 | lda #40 11 | jsr delay_msec 12 | jsr wait_vbl 13 | jsr disable_ppu 14 | jsr clear_vram 15 | jsr clear_sprites 16 | rts 17 | .code 18 | 19 | default_palette: 20 | .db $0f,$01,$02,$39 21 | .db $0f,$03,$04,$38 22 | .db $0f,$05,$06,$38 23 | .db $0f,$11,$12,$38 24 | 25 | .db $0f,$32,$32,$21 26 | .db $0f,$32,$32,$32 27 | .db $0f,$32,$32,$32 28 | .db $0f,$32,$32,$32 29 | .code 30 | 31 | default_tiles: 32 | .db $00,$00,$00,$00,$00,$00,$00,$00 33 | .db $00,$00,$00,$00,$00,$00,$00,$00 34 | 35 | .db $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff 36 | .db $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff 37 | 38 | .db $f0,$0f,$f0,$0f,$f0,$0f,$f0,$0f 39 | .db $f0,$0f,$f0,$0f,$f0,$0f,$f0,$0f 40 | 41 | .db $80,$00,$00,$00,$00,$00,$00,$00 42 | .db $80,$00,$00,$00,$00,$00,$00,$00 43 | 44 | .db $81,$00,$00,$00,$00,$00,$00,$81 45 | .db $81,$00,$00,$00,$00,$00,$00,$81 46 | .code 47 | 48 | clear_palette: 49 | jsr wait_vbl 50 | lda #$3f 51 | jsr set_vpage 52 | lda #$0f 53 | ldx #$20 54 | : sta $2007 55 | dex 56 | bne - 57 | rts 58 | .code 59 | 60 | ; A = # of tiles 61 | load_tiles: 62 | asl a 63 | asl a 64 | asl a 65 | asl a 66 | tax 67 | jsr wait_vbl 68 | lda #0 69 | jsr set_vpage 70 | ldy #0 71 | : lda tiles,y 72 | sta $2007 73 | iny 74 | dex 75 | bne - 76 | rts 77 | .code 78 | 79 | load_test_chr: 80 | jsr wait_vbl 81 | lda #0 82 | jsr set_vpage 83 | ldy #0 84 | : lda tiles,y 85 | sta $2007 86 | iny 87 | cpy #$40 88 | bne - 89 | rts 90 | .code 91 | 92 | load_palette: 93 | lda #$3f 94 | jsr set_vpage 95 | ldy #0 96 | : lda palette,y 97 | sta $2007 98 | iny 99 | cpy #$20 100 | bne - 101 | rts 102 | .code 103 | 104 | load_graphics: 105 | jsr wait_vbl 106 | lda #0 107 | sta $2000 108 | sta $2001 109 | jsr load_palette 110 | jsr load_test_chr 111 | jsr clear_nametable 112 | rts 113 | .code 114 | 115 | ; Determine current sprite addr by clearing sprite RAM 116 | ; modifying it and finding the modified byte. 117 | ; Returns address in Y, or $88 if search failed. 118 | determine_spr_addr: 119 | lda #$ff 120 | ldy #0 121 | : sta $2004 122 | dey 123 | bne - 124 | sty $2004 125 | : sty $2003 126 | bit $2004 127 | bpl spr_addr_found 128 | dey 129 | bne - 130 | ldy #$88 131 | spr_addr_found: 132 | rts 133 | .code 134 | 135 | ; Determine current VADDR by clearing VRAM then 136 | ; modifying it and finding the modified byte. 137 | ; Return address as $YYXX. 138 | determine_vaddr: 139 | lda #0 140 | ldx #$40 141 | ldy #0 142 | : sta $2007 143 | dey 144 | bne - 145 | dex 146 | bne - 147 | lda #$12 148 | sta $2007 149 | lda #0 150 | jmp find_changed_vram 151 | .code 152 | 153 | ; Find first byte in VRAM which doesn't match A. 154 | ; Return address as $YYXX. 155 | find_changed_vram: 156 | pha 157 | lda #0 158 | jsr set_vpage 159 | bit $2007 160 | pla 161 | ldx #0 162 | ldy #0 163 | : cmp $2007 164 | bne found 165 | inx 166 | bne - 167 | iny 168 | cpy #$24 169 | bne - 170 | found: 171 | rts 172 | .code 173 | 174 | delay_29781: 175 | ldy #141 ; 29769 delay 176 | lda #41 177 | jsr delay_ya1 178 | rts 179 | .code 180 | 181 | delay_29780: 182 | ldy #141 ; 29768 delay 183 | lda #41 184 | jsr delay_ya0 185 | rts 186 | .code 187 | -------------------------------------------------------------------------------- /test/sprite_overflow/source/prefix.a: -------------------------------------------------------------------------------- 1 | .include "prefix_ppu.a" 2 | 3 | sprites = $200 4 | 5 | ; Clear sprite table with $F8 6 | clear_sprite_table: 7 | lda #$F8 8 | ldx #0 9 | : sta sprites,x 10 | inx 11 | bne - 12 | rts 13 | .code 14 | 15 | ; Set every 4th sprite byte starting at X 16 | ; to A, modifying Y sprites in all. 17 | move_sprites: 18 | : sta sprites,x 19 | inx 20 | inx 21 | inx 22 | inx 23 | dey 24 | bne - 25 | rts 26 | 27 | ; Use DMA to copy sprite table to PPU 28 | ; Preserved: A, X, Y 29 | dma_sprite_table: 30 | pha 31 | lda #0 32 | sta $2003 33 | lda #$02 34 | sta $4014 35 | pla 36 | rts 37 | .code 38 | 39 | begin_sprite_overflow_tests: 40 | sei 41 | lda #0 42 | sta $2000 43 | sta $2001 44 | jsr wait_vbl 45 | jsr wait_vbl 46 | 47 | ; black palette 48 | lda #$3F 49 | jsr set_vpage 50 | ldy #$40 51 | lda #$0F 52 | : sta $2007 53 | dey 54 | bne - 55 | 56 | jsr clear_sprite_table 57 | rts 58 | .code 59 | 60 | begin_overflow_test: 61 | sta