move nes into the sources/NES directory
This commit is contained in:
BIN
data/MegaMan.nes
Normal file
BIN
data/MegaMan.nes
Normal file
Binary file not shown.
BIN
data/dk1.nes
Normal file
BIN
data/dk1.nes
Normal file
Binary file not shown.
BIN
data/dk2.nes
Normal file
BIN
data/dk2.nes
Normal file
Binary file not shown.
BIN
data/dk3.nes
Normal file
BIN
data/dk3.nes
Normal file
Binary file not shown.
BIN
data/smb1.nes
Normal file
BIN
data/smb1.nes
Normal file
Binary file not shown.
BIN
data/testsuite/blargg_cpu_tests/official.nes
Normal file
BIN
data/testsuite/blargg_cpu_tests/official.nes
Normal file
Binary file not shown.
BIN
data/testsuite/blargg_ppu_tests_2005.09.15b/palette_ram.nes
Normal file
BIN
data/testsuite/blargg_ppu_tests_2005.09.15b/palette_ram.nes
Normal file
Binary file not shown.
BIN
data/testsuite/blargg_ppu_tests_2005.09.15b/power_up_palette.nes
Normal file
BIN
data/testsuite/blargg_ppu_tests_2005.09.15b/power_up_palette.nes
Normal file
Binary file not shown.
74
data/testsuite/blargg_ppu_tests_2005.09.15b/readme.txt
Normal file
74
data/testsuite/blargg_ppu_tests_2005.09.15b/readme.txt
Normal file
@@ -0,0 +1,74 @@
|
||||
NTSC NES PPU Tests
|
||||
------------------
|
||||
These ROMs test a few aspects of the NTSC NES PPU operation. They have been
|
||||
tested on an actual NES and all give a passing result. I wrote them to verify
|
||||
that my NES emulator's PPU was working properly.
|
||||
|
||||
Each ROM runs several tests and reports a result code on screen and by beeping
|
||||
a number of times. A result code of 1 always indicates that all tests were
|
||||
passed; see below for the meaning of other codes for each test.
|
||||
|
||||
The main source code for each test is included, and most tests are clearly
|
||||
divided into sections. Some of the common support code is included, but not
|
||||
all, since it runs on a custom setup. Contact me if you want to assemble the
|
||||
tests yourself.
|
||||
|
||||
Shay Green <hotpop.com@blargg> (swap to e-mail)
|
||||
|
||||
|
||||
palette_ram
|
||||
-----------
|
||||
PPU palette RAM read/write and mirroring test
|
||||
|
||||
1) Tests passed
|
||||
2) Palette read shouldn't be buffered like other VRAM
|
||||
3) Palette write/read doesn't work
|
||||
4) Paletteshould be mirrored within $3f00-$3fff
|
||||
5) Write to $10 should be mirrored at $00
|
||||
6) Write to $00 should be mirrored at $10
|
||||
|
||||
|
||||
power_up_palette
|
||||
----------------
|
||||
Reports whether initial values in palette at power-up match those
|
||||
that my NES has. These values are probably unique to my NES.
|
||||
|
||||
1) Palette matches
|
||||
2) Palette differs from table
|
||||
|
||||
|
||||
sprite_ram
|
||||
----------
|
||||
Tests sprite RAM access via $2003, $2004, and $4014
|
||||
|
||||
1) Tests passed
|
||||
2) Basic read/write doesn't work
|
||||
3) Address should increment on $2004 write
|
||||
4) Address should not increment on $2004 read
|
||||
5) Third sprite bytes should be masked with $e3 on read
|
||||
6) $4014 DMA copy doesn't work at all
|
||||
7) $4014 DMA copy should start at value in $2003 and wrap
|
||||
8) $4014 DMA copy should leave value in $2003 intact
|
||||
|
||||
|
||||
vbl_clear_time
|
||||
--------------
|
||||
The VBL flag ($2002.7) is cleared by the PPU around 2270 CPU clocks
|
||||
after NMI occurs.
|
||||
|
||||
1) Tests passed
|
||||
2) VBL flag cleared too soon
|
||||
3) VBL flag cleared too late
|
||||
|
||||
|
||||
vram_access
|
||||
-----------
|
||||
Tests PPU VRAM read/write and internal read buffer operation
|
||||
|
||||
1) Tests passed
|
||||
2) VRAM reads should be delayed in a buffer
|
||||
3) Basic Write/read doesn't work
|
||||
4) Read buffer shouldn't be affected by VRAM write
|
||||
5) Read buffer shouldn't be affected by palette write
|
||||
6) Palette read should also read VRAM into read buffer
|
||||
7) "Shadow" VRAM read unaffected by palette transparent color mirroring
|
||||
@@ -0,0 +1,106 @@
|
||||
; PPU palette RAM read/write and mirroring test
|
||||
; to do: check that upper two bits aren't stored
|
||||
|
||||
.include "prefix_ppu.a"
|
||||
|
||||
; Set VRAM address to $3f00 + X
|
||||
; Preserved: A, X, Y
|
||||
set_pal_addr:
|
||||
pha
|
||||
bit $2002
|
||||
lda #$3f
|
||||
sta $2006
|
||||
txa
|
||||
sta $2006
|
||||
pla
|
||||
rts
|
||||
|
||||
; Set palette entry X to A
|
||||
; Preserved: A, X, Y
|
||||
set_pal_entry:
|
||||
jsr set_pal_addr
|
||||
sta $2007
|
||||
rts
|
||||
|
||||
; Read palette entry X into A
|
||||
; Preserved: X, Y
|
||||
get_pal_entry:
|
||||
jsr set_pal_addr
|
||||
lda $2007
|
||||
and #$3f
|
||||
rts
|
||||
|
||||
reset:
|
||||
lda #50
|
||||
jsr delay_msec
|
||||
|
||||
jsr wait_vbl
|
||||
lda #0
|
||||
sta $2000
|
||||
sta $2001
|
||||
|
||||
lda #2;) Palette read shouldn't be buffered like other VRAM
|
||||
sta result
|
||||
ldx #$00
|
||||
lda #$12
|
||||
jsr set_pal_entry
|
||||
lda #$34
|
||||
sta $2007
|
||||
jsr get_pal_entry
|
||||
lda $2007
|
||||
cmp #$12
|
||||
jsr error_if_eq
|
||||
|
||||
lda #3;) Palette write/read doesn't work
|
||||
sta result
|
||||
ldx #$00
|
||||
lda #$34
|
||||
jsr set_pal_entry
|
||||
jsr get_pal_entry
|
||||
lda $2007
|
||||
cmp #$34
|
||||
jsr error_if_ne
|
||||
|
||||
lda #4;) Palette should be mirrored within $3f00-$3fff
|
||||
sta result
|
||||
ldx #$00
|
||||
lda #$12
|
||||
jsr set_pal_entry
|
||||
ldx #$e0
|
||||
lda #$34
|
||||
jsr set_pal_entry
|
||||
ldx #$00
|
||||
jsr get_pal_entry
|
||||
cmp #$34
|
||||
jsr error_if_ne
|
||||
|
||||
lda #5;) Write to $10 should be mirrored at $00
|
||||
sta result
|
||||
ldx #$00
|
||||
lda #$12
|
||||
jsr set_pal_entry
|
||||
ldx #$10
|
||||
lda #$34
|
||||
jsr set_pal_entry
|
||||
ldx #$00
|
||||
jsr get_pal_entry
|
||||
cmp #$34
|
||||
jsr error_if_ne
|
||||
|
||||
lda #6;) Write to $00 should be mirrored at $10
|
||||
sta result
|
||||
ldx #$10
|
||||
lda #$12
|
||||
jsr set_pal_entry
|
||||
ldx #$00
|
||||
lda #$34
|
||||
jsr set_pal_entry
|
||||
ldx #$10
|
||||
jsr get_pal_entry
|
||||
cmp #$34
|
||||
jsr error_if_ne
|
||||
|
||||
lda #1;) Tests passed
|
||||
sta result
|
||||
jmp report_final_result
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
; Reports whether initial values in palette at power-up match those
|
||||
; that my NES has. These values are probably unique to my NES.
|
||||
|
||||
.include "prefix_ppu.a"
|
||||
|
||||
reset:
|
||||
lda #50
|
||||
jsr delay_msec
|
||||
|
||||
jsr wait_vbl
|
||||
lda #0
|
||||
sta $2000
|
||||
sta $2001
|
||||
|
||||
lda #2;) Palette differs from table
|
||||
sta result
|
||||
lda #$3f
|
||||
sta $2006
|
||||
lda #$00
|
||||
sta $2006
|
||||
ldx #0
|
||||
: lda $2007
|
||||
cmp table,x
|
||||
jsr error_if_ne
|
||||
inx
|
||||
cpx #$20
|
||||
bne -
|
||||
|
||||
lda #1;) Palette matches
|
||||
sta result
|
||||
jmp report_final_result
|
||||
|
||||
table:
|
||||
.db $09,$01,$00,$01,$00,$02,$02,$0D,$08,$10,$08,$24,$00,$00,$04,$2C
|
||||
.db $09,$01,$34,$03,$00,$04,$00,$14,$08,$3A,$00,$02,$00,$20,$2C,$08
|
||||
@@ -0,0 +1,48 @@
|
||||
; Prefix included at the beginning of each test. Defines vectors
|
||||
; and other things for custom devcart loader.
|
||||
;
|
||||
; Reset vector points to "reset".
|
||||
; NMI points to "nmi" if defined, otherwise default_nmi.
|
||||
; IRQ points to "irq" if defined, otherwise default_irq.
|
||||
|
||||
default_nmi:
|
||||
rti
|
||||
|
||||
default_irq:
|
||||
bit $4015
|
||||
rti
|
||||
|
||||
; Delays for almost A milliseconds (A * 0.999009524 msec)
|
||||
; Preserved: X, Y
|
||||
delay_msec:
|
||||
|
||||
; Delays for almost 'A / 10' milliseconds (A * 0.099453968 msec)
|
||||
; Preserved: X, Y
|
||||
delay_01_msec:
|
||||
|
||||
; Variable delay. All calls include comment stating number of clocks
|
||||
; used, including the setup code in the caller.
|
||||
delay_yaNN:
|
||||
|
||||
; Report value in low-mem variable 'result' as number of beeps and
|
||||
; code printed to console, then jump to forever.
|
||||
report_final_result:
|
||||
|
||||
; Disable IRQ and NMI then loop endlessly.
|
||||
forever:
|
||||
|
||||
|
||||
; Report error if last result was non-zero
|
||||
error_if_ne:
|
||||
bne error_if_
|
||||
rts
|
||||
|
||||
; Report error if last result was zero
|
||||
error_if_eq:
|
||||
beq error_if_
|
||||
rts
|
||||
|
||||
; Report error
|
||||
error_if_:
|
||||
jmp report_final_result
|
||||
|
||||
@@ -0,0 +1,131 @@
|
||||
; Tests sprite RAM access via $2003, $2004, and $4014
|
||||
|
||||
.include "prefix_ppu.a"
|
||||
|
||||
sprites = $200
|
||||
|
||||
reset:
|
||||
lda #50
|
||||
jsr delay_msec
|
||||
|
||||
jsr wait_vbl
|
||||
lda #0
|
||||
sta $2000
|
||||
sta $2001
|
||||
|
||||
lda #2;) Basic read/write doesn't work
|
||||
sta result
|
||||
lda #0
|
||||
sta $2003
|
||||
lda #$12
|
||||
sta $2004
|
||||
lda #0
|
||||
sta $2003
|
||||
lda $2004
|
||||
cmp #$12
|
||||
jsr error_if_ne
|
||||
|
||||
lda #3;) Address should increment on $2004 write
|
||||
sta result
|
||||
lda #0
|
||||
sta $2003
|
||||
lda #$12
|
||||
sta $2004
|
||||
lda #$34
|
||||
sta $2004
|
||||
lda #1
|
||||
sta $2003
|
||||
lda $2004
|
||||
cmp #$34
|
||||
jsr error_if_ne
|
||||
|
||||
lda #4;) Address should not increment on $2004 read
|
||||
sta result
|
||||
lda #0
|
||||
sta $2003
|
||||
lda #$12
|
||||
sta $2004
|
||||
lda #$34
|
||||
sta $2004
|
||||
lda #0
|
||||
sta $2003
|
||||
lda $2004
|
||||
lda $2004
|
||||
cmp #$34
|
||||
jsr error_if_eq
|
||||
|
||||
lda #5;) Third sprite bytes should be masked with $e3 on read
|
||||
sta result
|
||||
lda #3
|
||||
sta $2003
|
||||
lda #$ff
|
||||
sta $2004
|
||||
lda #3
|
||||
sta $2003
|
||||
lda $2004
|
||||
cmp #$e3
|
||||
jsr error_if_eq
|
||||
|
||||
lda #6;) $4014 DMA copy doesn't work at all
|
||||
sta result
|
||||
ldx #0 ; set up data to copy from
|
||||
: lda test_data,x
|
||||
sta sprites,x
|
||||
inx
|
||||
cpx #4
|
||||
bne -
|
||||
lda #0 ; dma copy
|
||||
sta $2003
|
||||
lda #$02
|
||||
sta $4014
|
||||
ldx #0 ; set up data to copy from
|
||||
: stx $2003
|
||||
lda $2004
|
||||
cmp test_data,x
|
||||
jsr error_if_ne
|
||||
inx
|
||||
cpx #4
|
||||
bne -
|
||||
|
||||
lda #7;) $4014 DMA copy should start at value in $2003 and wrap
|
||||
sta result
|
||||
ldx #0 ; set up data to copy from
|
||||
: lda test_data,x
|
||||
sta sprites,x
|
||||
inx
|
||||
cpx #4
|
||||
bne -
|
||||
lda #1 ; dma copy
|
||||
sta $2003
|
||||
lda #$02
|
||||
sta $4014
|
||||
ldx #1 ; set up data to copy from
|
||||
: stx $2003
|
||||
lda $2004
|
||||
cmp sprites - 1,x
|
||||
jsr error_if_ne
|
||||
inx
|
||||
cpx #5
|
||||
bne -
|
||||
|
||||
lda #8;) $4014 DMA copy should leave value in $2003 intact
|
||||
sta result
|
||||
lda #1 ; dma copy
|
||||
sta $2003
|
||||
lda #$02
|
||||
sta $4014
|
||||
lda #$ff
|
||||
sta $2004
|
||||
lda #1
|
||||
sta $2003
|
||||
lda $2004
|
||||
cmp #$ff
|
||||
jsr error_if_ne
|
||||
|
||||
lda #1;) Tests passed
|
||||
sta result
|
||||
jmp report_final_result
|
||||
|
||||
test_data:
|
||||
.db $12,$82,$e3,$78
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
; The VBL flag ($2002.7) is cleared by the PPU around 2270 CPU clocks
|
||||
; after NMI occurs.
|
||||
|
||||
.include "prefix_ppu.a"
|
||||
|
||||
phase = 10
|
||||
|
||||
reset:
|
||||
lda #100
|
||||
jsr delay_msec
|
||||
|
||||
lda #1
|
||||
sta phase
|
||||
|
||||
jsr wait_vbl
|
||||
lda #$80
|
||||
sta $2000
|
||||
lda #$00
|
||||
sta $2001
|
||||
|
||||
wait: jmp wait
|
||||
|
||||
nmi: ; 7 clocks for NMI vectoring
|
||||
ldy #203 ; 2251 delay
|
||||
lda #1
|
||||
jsr delay_ya1
|
||||
|
||||
dec phase ; 5
|
||||
bne + ; 3
|
||||
; -1
|
||||
|
||||
lda $2002 ; read at 2268
|
||||
ldx #2;) VBL flag cleared too soon
|
||||
stx result
|
||||
and #$80
|
||||
jsr error_if_eq
|
||||
jmp wait
|
||||
|
||||
: bit <0
|
||||
lda $2002 ; read at 2272
|
||||
ldx #3;) VBL flag cleared too late
|
||||
stx result
|
||||
and #$80
|
||||
jsr error_if_ne
|
||||
|
||||
lda #1;) Tests passed
|
||||
sta result
|
||||
jmp report_final_result
|
||||
@@ -0,0 +1,143 @@
|
||||
; Tests PPU VRAM read/write and internal read buffer operation
|
||||
|
||||
.include "prefix_ppu.a"
|
||||
|
||||
; Set VRAM addr to $2f00 + A
|
||||
; Preserved: A, X, Y
|
||||
set_vram_pos:
|
||||
pha
|
||||
lda #$2f
|
||||
sta $2006
|
||||
pla
|
||||
sta $2006
|
||||
rts
|
||||
|
||||
reset:
|
||||
lda #50
|
||||
jsr delay_msec
|
||||
|
||||
jsr wait_vbl
|
||||
lda #0
|
||||
sta $2000
|
||||
sta $2001
|
||||
|
||||
lda #2;) VRAM reads should be delayed in a buffer
|
||||
sta result
|
||||
lda #$00
|
||||
jsr set_vram_pos
|
||||
lda #$12
|
||||
sta $2007
|
||||
lda #$34
|
||||
sta $2007
|
||||
lda #$00
|
||||
jsr set_vram_pos
|
||||
lda $2007
|
||||
lda $2007
|
||||
cmp #$34
|
||||
jsr error_if_eq
|
||||
|
||||
lda #3;) Basic Write/read doesn't work
|
||||
sta result
|
||||
lda #$00
|
||||
jsr set_vram_pos
|
||||
lda #$56
|
||||
sta $2007
|
||||
lda #$00
|
||||
jsr set_vram_pos
|
||||
lda $2007
|
||||
lda $2007
|
||||
cmp #$56
|
||||
jsr error_if_ne
|
||||
|
||||
lda #4;) Read buffer shouldn't be affected by VRAM write
|
||||
sta result
|
||||
lda #$00
|
||||
jsr set_vram_pos
|
||||
lda #$78
|
||||
sta $2007
|
||||
lda #$00
|
||||
jsr set_vram_pos
|
||||
lda #$00
|
||||
lda $2007 ; buffer now contains $78
|
||||
lda #$12
|
||||
sta $2007 ; shouldn't affect buffer
|
||||
lda $2007
|
||||
cmp #$78
|
||||
jsr error_if_ne
|
||||
|
||||
lda #5;) Read buffer shouldn't be affected by palette write
|
||||
sta result
|
||||
lda #$00
|
||||
jsr set_vram_pos
|
||||
lda #$9a
|
||||
sta $2007
|
||||
lda #$00
|
||||
jsr set_vram_pos
|
||||
lda $2007 ; buffer now contains $9a
|
||||
lda #$3f
|
||||
sta $2006
|
||||
lda #$00
|
||||
sta $2006
|
||||
lda #$34
|
||||
sta $2007 ; shouldn't affect buffer
|
||||
lda #$01 ; change back to non-palette addr to enable buffer
|
||||
jsr set_vram_pos
|
||||
lda $2007
|
||||
cmp #$9a
|
||||
jsr error_if_ne
|
||||
|
||||
lda #6;) Palette read should also read VRAM into read buffer
|
||||
sta result
|
||||
lda #$12
|
||||
jsr set_vram_pos
|
||||
lda #$9a
|
||||
sta $2007
|
||||
lda $2007
|
||||
lda #$3f
|
||||
sta $2006
|
||||
lda #$12
|
||||
sta $2006
|
||||
lda $2007 ; fills buffer with VRAM hidden by palette
|
||||
lda #$13 ; change back to non-palette addr to enable buffer
|
||||
jsr set_vram_pos
|
||||
lda $2007
|
||||
cmp #$9a
|
||||
jsr error_if_ne
|
||||
|
||||
lda #7;) "Shadow" VRAM read unaffected by palette mirroring
|
||||
sta result
|
||||
lda #$04
|
||||
jsr set_vram_pos
|
||||
lda #$12
|
||||
sta $2007
|
||||
lda #$14
|
||||
jsr set_vram_pos
|
||||
lda #$34
|
||||
sta $2007
|
||||
lda #$3f
|
||||
sta $2006
|
||||
lda #$04
|
||||
sta $2006
|
||||
lda $2007 ; fills buffer with VRAM hidden by palette
|
||||
lda #$13 ; change back to non-palette addr to enable buffer
|
||||
jsr set_vram_pos
|
||||
lda $2007
|
||||
cmp #$12
|
||||
jsr error_if_ne
|
||||
lda #$34
|
||||
sta $2007
|
||||
lda #$3f
|
||||
sta $2006
|
||||
lda #$14
|
||||
sta $2006
|
||||
lda $2007 ; fills buffer with VRAM hidden by palette
|
||||
lda #$13 ; change back to non-palette addr to enable buffer
|
||||
jsr set_vram_pos
|
||||
lda $2007
|
||||
cmp #$34
|
||||
jsr error_if_ne
|
||||
|
||||
lda #1;) Tests passed
|
||||
sta result
|
||||
jmp report_final_result
|
||||
|
||||
BIN
data/testsuite/blargg_ppu_tests_2005.09.15b/sprite_ram.nes
Normal file
BIN
data/testsuite/blargg_ppu_tests_2005.09.15b/sprite_ram.nes
Normal file
Binary file not shown.
BIN
data/testsuite/blargg_ppu_tests_2005.09.15b/vbl_clear_time.nes
Normal file
BIN
data/testsuite/blargg_ppu_tests_2005.09.15b/vbl_clear_time.nes
Normal file
Binary file not shown.
BIN
data/testsuite/blargg_ppu_tests_2005.09.15b/vram_access.nes
Normal file
BIN
data/testsuite/blargg_ppu_tests_2005.09.15b/vram_access.nes
Normal file
Binary file not shown.
BIN
data/testsuite/nestest/nestest.nes
Normal file
BIN
data/testsuite/nestest/nestest.nes
Normal file
Binary file not shown.
724
data/testsuite/nestest/nestest.txt
Normal file
724
data/testsuite/nestest/nestest.txt
Normal file
@@ -0,0 +1,724 @@
|
||||
The ultimate NES CPU test ROM
|
||||
-----------------------------
|
||||
|
||||
|
||||
V1.00 - 09/06/04
|
||||
|
||||
By: Kevin Horton
|
||||
|
||||
|
||||
---
|
||||
|
||||
|
||||
What it is:
|
||||
|
||||
This here is a pretty much all inclusive test suite for a NES CPU.
|
||||
It was designed to test almost every combination of flags, instructions,
|
||||
and registers. Some of these tests are very difficult, and so far,
|
||||
Nesten and Nesticle failed it. Nintendulator passes, as does a real
|
||||
NES (naturally). I haven't tested it with any more emualtors yet.
|
||||
|
||||
I attempted to check the states of all flags after most instructions.
|
||||
For example, CPY and CMP shouldn't affect the overflow flag, while SBC
|
||||
and ADC should. Likewise, all forms of wrapping ARE tested for- zeropage
|
||||
wrapping being the tests most emulators fail.
|
||||
|
||||
i.e.
|
||||
|
||||
LDA #001h
|
||||
LDA 0ffh,X ;indexed zeropage read
|
||||
|
||||
should read the byte from 000h... NOT 0100h. This is because zeropage
|
||||
instructions cannot cross a page boundary.
|
||||
|
||||
---
|
||||
|
||||
How to work it good:
|
||||
|
||||
Simply run the .NES ROM on your emulator of choice. You can select a single
|
||||
test to run, or you can run ALL tests in sequence by selecting the
|
||||
appropriate option.
|
||||
|
||||
Pressing Select will change pages and allow testing "invalid" opcodes.
|
||||
Be aware that these will crash alot of emulators <cough>Nesten<cough>.
|
||||
|
||||
Once a test completes, the result will be "OK" if the test passes, or a
|
||||
2 digit hex number which indicates a failure of some kind. A list is
|
||||
provided below for the failure and its cause. For a more detailed reason
|
||||
for the failure, you should check out the .ASM file included with this
|
||||
document.
|
||||
|
||||
If the entire page of tests succeeds, "OK" will be displayed next to the
|
||||
first entry on the page. If one or more tests fails, "Er" will be displayed
|
||||
instead.
|
||||
|
||||
---
|
||||
|
||||
NSF player testing:
|
||||
|
||||
This ROM is set up to be usable inside an NSF player. It outputs the
|
||||
results of the test audially. <to be finished>
|
||||
|
||||
---
|
||||
|
||||
Emulator authors:
|
||||
|
||||
This test program, when run on "automation", (i.e. set your program counter
|
||||
to 0c000h) will perform all tests in sequence and shove the results of
|
||||
the tests into locations 02h and 03h.
|
||||
|
||||
---
|
||||
|
||||
Final notes:
|
||||
|
||||
The hex numbers shown on the screen (or stored in the above mentioned
|
||||
memory locations) are of the LAST test that failed in the group tested.
|
||||
This means, there could be multiple failures in one or more groups. This
|
||||
wasn't the best solution, but since there are close to 400 tests performed,
|
||||
any other way wouldn't have had acceptable memory usage. So long as your
|
||||
emulator bugs are fixed and the numbers are getting smaller, you're doing
|
||||
good :-)
|
||||
|
||||
|
||||
----------------------------------------
|
||||
|
||||
Test failure codes and what they mean:
|
||||
|
||||
(byte 02h only)
|
||||
|
||||
000h - tests completed successfully
|
||||
|
||||
branch tests
|
||||
------------
|
||||
001h - BCS failed to branch
|
||||
002h - BCS branched when it shouldn't have
|
||||
003h - BCC branched when it shouldn't have
|
||||
004h - BCC failed to branch
|
||||
005h - BEQ failed to branch
|
||||
006h - BEQ branched when it shouldn't have
|
||||
007h - BNE failed to branch
|
||||
008h - BNE branched when it shouldn't have
|
||||
009h - BVS failed to branch
|
||||
00Ah - BVC branched when it shouldn't have
|
||||
00Bh - BVC failed to branch
|
||||
00Ch - BVS branched when it shouldn't have
|
||||
00Dh - BPL failed to branch
|
||||
00Eh - BPL branched when it shouldn't have
|
||||
00Fh - BMI failed to branch
|
||||
010h - BMI branched when it shouldn't have
|
||||
|
||||
flag tests
|
||||
----------
|
||||
011h - PHP/flags failure (bits set)
|
||||
012h - PHP/flags failure (bits clear)
|
||||
013h - PHP/flags failure (misc bit states)
|
||||
014h - PLP/flags failure (misc bit states)
|
||||
015h - PLP/flags failure (misc bit states)
|
||||
016h - PHA/PLA failure (PLA didn't affect Z and N properly)
|
||||
017h - PHA/PLA failure (PLA didn't affect Z and N properly)
|
||||
|
||||
immediate instruction tests
|
||||
---------------------------
|
||||
018h - ORA # failure
|
||||
019h - ORA # failure
|
||||
01Ah - AND # failure
|
||||
01Bh - AND # failure
|
||||
01Ch - EOR # failure
|
||||
01Dh - EOR # failure
|
||||
01Eh - ADC # failure (overflow/carry problems)
|
||||
01Fh - ADC # failure (decimal mode was turned on)
|
||||
020h - ADC # failure
|
||||
021h - ADC # failure
|
||||
022h - ADC # failure
|
||||
023h - LDA # failure (didn't set N and Z correctly)
|
||||
024h - LDA # failure (didn't set N and Z correctly)
|
||||
025h - CMP # failure (messed up flags)
|
||||
026h - CMP # failure (messed up flags)
|
||||
027h - CMP # failure (messed up flags)
|
||||
028h - CMP # failure (messed up flags)
|
||||
029h - CMP # failure (messed up flags)
|
||||
02Ah - CMP # failure (messed up flags)
|
||||
02Bh - CPY # failure (messed up flags)
|
||||
02Ch - CPY # failure (messed up flags)
|
||||
02Dh - CPY # failure (messed up flags)
|
||||
02Eh - CPY # failure (messed up flags)
|
||||
02Fh - CPY # failure (messed up flags)
|
||||
030h - CPY # failure (messed up flags)
|
||||
031h - CPY # failure (messed up flags)
|
||||
032h - CPX # failure (messed up flags)
|
||||
033h - CPX # failure (messed up flags)
|
||||
034h - CPX # failure (messed up flags)
|
||||
035h - CPX # failure (messed up flags)
|
||||
036h - CPX # failure (messed up flags)
|
||||
037h - CPX # failure (messed up flags)
|
||||
038h - CPX # failure (messed up flags)
|
||||
039h - LDX # failure (didn't set N and Z correctly)
|
||||
03Ah - LDX # failure (didn't set N and Z correctly)
|
||||
03Bh - LDY # failure (didn't set N and Z correctly)
|
||||
03Ch - LDY # failure (didn't set N and Z correctly)
|
||||
03Dh - compare(s) stored the result in a register (whoops!)
|
||||
071h - SBC # failure
|
||||
072h - SBC # failure
|
||||
073h - SBC # failure
|
||||
074h - SBC # failure
|
||||
075h - SBC # failure
|
||||
|
||||
|
||||
implied instruction tests
|
||||
-------------------------
|
||||
03Eh - INX/DEX/INY/DEY did something bad
|
||||
03Fh - INY/DEY messed up overflow or carry
|
||||
040h - INX/DEX messed up overflow or carry
|
||||
041h - TAY did something bad (changed wrong regs, messed up flags)
|
||||
042h - TAX did something bad (changed wrong regs, messed up flags)
|
||||
043h - TYA did something bad (changed wrong regs, messed up flags)
|
||||
044h - TXA did something bad (changed wrong regs, messed up flags)
|
||||
045h - TXS didn't set flags right, or TSX touched flags and it shouldn't have
|
||||
|
||||
stack tests
|
||||
-----------
|
||||
046h - wrong data popped, or data not in right location on stack
|
||||
047h - JSR didn't work as expected
|
||||
048h - RTS/JSR shouldn't have affected flags
|
||||
049h - RTI/RTS didn't work right when return addys/data were manually pushed
|
||||
|
||||
accumulator tests
|
||||
-----------------
|
||||
04Ah - LSR A failed
|
||||
04Bh - ASL A failed
|
||||
04Ch - ROR A failed
|
||||
04Dh - ROL A failed
|
||||
|
||||
(indirect,x) tests
|
||||
------------------
|
||||
058h - LDA didn't load the data it expected to load
|
||||
059h - STA didn't store the data where it was supposed to
|
||||
05Ah - ORA failure
|
||||
05Bh - ORA failure
|
||||
05Ch - AND failure
|
||||
05Dh - AND failure
|
||||
05Eh - EOR failure
|
||||
05Fh - EOR failure
|
||||
060h - ADC failure
|
||||
061h - ADC failure
|
||||
062h - ADC failure
|
||||
063h - ADC failure
|
||||
064h - ADC failure
|
||||
065h - CMP failure
|
||||
066h - CMP failure
|
||||
067h - CMP failure
|
||||
068h - CMP failure
|
||||
069h - CMP failure
|
||||
06Ah - CMP failure
|
||||
06Bh - CMP failure
|
||||
06Ch - SBC failure
|
||||
06Dh - SBC failure
|
||||
06Eh - SBC failure
|
||||
06Fh - SBC failure
|
||||
070h - SBC failure
|
||||
|
||||
zeropage tests
|
||||
--------------
|
||||
076h - LDA didn't set the flags properly
|
||||
077h - STA affected flags it shouldn't
|
||||
078h - LDY didn't set the flags properly
|
||||
079h - STY affected flags it shouldn't
|
||||
07Ah - LDX didn't set the flags properly
|
||||
07Bh - STX affected flags it shouldn't
|
||||
07Ch - BIT failure
|
||||
07Dh - BIT failure
|
||||
07Eh - ORA failure
|
||||
07Fh - ORA failure
|
||||
080h - AND failure
|
||||
081h - AND failure
|
||||
082h - EOR failure
|
||||
083h - EOR failure
|
||||
084h - ADC failure
|
||||
085h - ADC failure
|
||||
086h - ADC failure
|
||||
087h - ADC failure
|
||||
088h - ADC failure
|
||||
089h - CMP failure
|
||||
08Ah - CMP failure
|
||||
08Bh - CMP failure
|
||||
08Ch - CMP failure
|
||||
08Dh - CMP failure
|
||||
08Eh - CMP failure
|
||||
08Fh - CMP failure
|
||||
090h - SBC failure
|
||||
091h - SBC failure
|
||||
092h - SBC failure
|
||||
093h - SBC failure
|
||||
094h - SBC failure
|
||||
095h - CPX failure
|
||||
096h - CPX failure
|
||||
097h - CPX failure
|
||||
098h - CPX failure
|
||||
099h - CPX failure
|
||||
09Ah - CPX failure
|
||||
09Bh - CPX failure
|
||||
09Ch - CPY failure
|
||||
09Dh - CPY failure
|
||||
09Eh - CPY failure
|
||||
09Fh - CPY failure
|
||||
0A0h - CPY failure
|
||||
0A1h - CPY failure
|
||||
0A2h - CPY failure
|
||||
0A3h - LSR failure
|
||||
0A4h - LSR failure
|
||||
0A5h - ASL failure
|
||||
0A6h - ASL failure
|
||||
0A7h - ROL failure
|
||||
0A8h - ROL failure
|
||||
0A9h - ROR failure
|
||||
0AAh - ROR failure
|
||||
0ABh - INC failure
|
||||
0ACh - INC failure
|
||||
0ADh - DEC failure
|
||||
0AEh - DEC failure
|
||||
0AFh - DEC failure
|
||||
|
||||
Absolute tests
|
||||
--------------
|
||||
0B0h - LDA didn't set the flags properly
|
||||
0B1h - STA affected flags it shouldn't
|
||||
0B2h - LDY didn't set the flags properly
|
||||
0B3h - STY affected flags it shouldn't
|
||||
0B4h - LDX didn't set the flags properly
|
||||
0B5h - STX affected flags it shouldn't
|
||||
0B6h - BIT failure
|
||||
0B7h - BIT failure
|
||||
0B8h - ORA failure
|
||||
0B9h - ORA failure
|
||||
0BAh - AND failure
|
||||
0BBh - AND failure
|
||||
0BCh - EOR failure
|
||||
0BDh - EOR failure
|
||||
0BEh - ADC failure
|
||||
0BFh - ADC failure
|
||||
0C0h - ADC failure
|
||||
0C1h - ADC failure
|
||||
0C2h - ADC failure
|
||||
0C3h - CMP failure
|
||||
0C4h - CMP failure
|
||||
0C5h - CMP failure
|
||||
0C6h - CMP failure
|
||||
0C7h - CMP failure
|
||||
0C8h - CMP failure
|
||||
0C9h - CMP failure
|
||||
0CAh - SBC failure
|
||||
0CBh - SBC failure
|
||||
0CCh - SBC failure
|
||||
0CDh - SBC failure
|
||||
0CEh - SBC failure
|
||||
0CFh - CPX failure
|
||||
0D0h - CPX failure
|
||||
0D1h - CPX failure
|
||||
0D2h - CPX failure
|
||||
0D3h - CPX failure
|
||||
0D4h - CPX failure
|
||||
0D5h - CPX failure
|
||||
0D6h - CPY failure
|
||||
0D7h - CPY failure
|
||||
0D8h - CPY failure
|
||||
0D9h - CPY failure
|
||||
0DAh - CPY failure
|
||||
0DBh - CPY failure
|
||||
0DCh - CPY failure
|
||||
0DDh - LSR failure
|
||||
0DEh - LSR failure
|
||||
0DFh - ASL failure
|
||||
0E0h - ASL failure
|
||||
0E1h - ROR failure
|
||||
0E2h - ROR failure
|
||||
0E3h - ROL failure
|
||||
0E4h - ROL failure
|
||||
0E5h - INC failure
|
||||
0E6h - INC failure
|
||||
0E7h - DEC failure
|
||||
0E8h - DEC failure
|
||||
0E9h - DEC failure
|
||||
|
||||
(indirect),y tests
|
||||
------------------
|
||||
0EAh - LDA didn't load what it was supposed to
|
||||
0EBh - read location should've wrapped around ffffh to 0000h
|
||||
0ECh - should've wrapped zeropage address
|
||||
0EDh - ORA failure
|
||||
0EEh - ORA failure
|
||||
0EFh - AND failure
|
||||
0F0h - AND failure
|
||||
0F1h - EOR failure
|
||||
0F2h - EOR failure
|
||||
0F3h - ADC failure
|
||||
0F4h - ADC failure
|
||||
0F5h - ADC failure
|
||||
0F6h - ADC failure
|
||||
0F7h - ADC failure
|
||||
0F8h - CMP failure
|
||||
0F9h - CMP failure
|
||||
0FAh - CMP failure
|
||||
0FBh - CMP failure
|
||||
0FCh - CMP failure
|
||||
0FDh - CMP failure
|
||||
0FEh - CMP failure
|
||||
|
||||
(error byte location 03h starts here)
|
||||
|
||||
000h - no error, all tests pass
|
||||
001h - SBC failure
|
||||
002h - SBC failure
|
||||
003h - SBC failure
|
||||
004h - SBC failure
|
||||
005h - SBC failure
|
||||
006h - STA failure
|
||||
007h - JMP () data reading didn't wrap properly (this fails on a 65C02)
|
||||
|
||||
zeropage,x tests
|
||||
----------------
|
||||
008h - LDY,X failure
|
||||
009h - LDY,X failure
|
||||
00Ah - STY,X failure
|
||||
00Bh - ORA failure
|
||||
00Ch - ORA failure
|
||||
00Dh - AND failure
|
||||
00Eh - AND failure
|
||||
00Fh - EOR failure
|
||||
010h - EOR failure
|
||||
011h - ADC failure
|
||||
012h - ADC failure
|
||||
013h - ADC failure
|
||||
014h - ADC failure
|
||||
015h - ADC failure
|
||||
016h - CMP failure
|
||||
017h - CMP failure
|
||||
018h - CMP failure
|
||||
019h - CMP failure
|
||||
01Ah - CMP failure
|
||||
01Bh - CMP failure
|
||||
01Ch - CMP failure
|
||||
01Dh - SBC failure
|
||||
01Eh - SBC failure
|
||||
01Fh - SBC failure
|
||||
020h - SBC failure
|
||||
021h - SBC failure
|
||||
022h - LDA failure
|
||||
023h - LDA failure
|
||||
024h - STA failure
|
||||
025h - LSR failure
|
||||
026h - LSR failure
|
||||
027h - ASL failure
|
||||
028h - ASL failure
|
||||
029h - ROR failure
|
||||
02Ah - ROR failure
|
||||
02Bh - ROL failure
|
||||
02Ch - ROL failure
|
||||
02Dh - INC failure
|
||||
02Eh - INC failure
|
||||
02Fh - DEC failure
|
||||
030h - DEC failure
|
||||
031h - DEC failure
|
||||
032h - LDX,Y failure
|
||||
033h - LDX,Y failure
|
||||
034h - STX,Y failure
|
||||
035h - STX,Y failure
|
||||
|
||||
absolute,y tests
|
||||
----------------
|
||||
036h - LDA failure
|
||||
037h - LDA failure to wrap properly from ffffh to 0000h
|
||||
038h - LDA failure, page cross
|
||||
039h - ORA failure
|
||||
03Ah - ORA failure
|
||||
03Bh - AND failure
|
||||
03Ch - AND failure
|
||||
03Dh - EOR failure
|
||||
03Eh - EOR failure
|
||||
03Fh - ADC failure
|
||||
040h - ADC failure
|
||||
041h - ADC failure
|
||||
042h - ADC failure
|
||||
043h - ADC failure
|
||||
044h - CMP failure
|
||||
045h - CMP failure
|
||||
046h - CMP failure
|
||||
047h - CMP failure
|
||||
048h - CMP failure
|
||||
049h - CMP failure
|
||||
04Ah - CMP failure
|
||||
04Bh - SBC failure
|
||||
04Ch - SBC failure
|
||||
04Dh - SBC failure
|
||||
04Eh - SBC failure
|
||||
04Fh - SBC failure
|
||||
050h - STA failure
|
||||
|
||||
absolute,x tests
|
||||
----------------
|
||||
051h - LDY,X failure
|
||||
052h - LDY,X failure (didn't page cross)
|
||||
053h - ORA failure
|
||||
054h - ORA failure
|
||||
055h - AND failure
|
||||
056h - AND failure
|
||||
057h - EOR failure
|
||||
058h - EOR failure
|
||||
059h - ADC failure
|
||||
05Ah - ADC failure
|
||||
05Bh - ADC failure
|
||||
05Ch - ADC failure
|
||||
05Dh - ADC failure
|
||||
05Eh - CMP failure
|
||||
05Fh - CMP failure
|
||||
060h - CMP failure
|
||||
061h - CMP failure
|
||||
062h - CMP failure
|
||||
063h - CMP failure
|
||||
064h - CMP failure
|
||||
065h - SBC failure
|
||||
066h - SBC failure
|
||||
067h - SBC failure
|
||||
068h - SBC failure
|
||||
069h - SBC failure
|
||||
06Ah - LDA failure
|
||||
06Bh - LDA failure (didn't page cross)
|
||||
06Ch - STA failure
|
||||
06Dh - LSR failure
|
||||
06Eh - LSR failure
|
||||
06Fh - ASL failure
|
||||
070h - ASL failure
|
||||
071h - ROR failure
|
||||
072h - ROR failure
|
||||
073h - ROL failure
|
||||
074h - ROL failure
|
||||
075h - INC failure
|
||||
076h - INC failure
|
||||
077h - DEC failure
|
||||
078h - DEC failure
|
||||
079h - DEC failure
|
||||
07Ah - LDX,Y failure
|
||||
07Bh - LDX,Y failure
|
||||
|
||||
------------------------------------
|
||||
|
||||
Invalid opcode tests... all errors are reported in byte 03h unless
|
||||
specified.
|
||||
|
||||
NOP - "invalid" opcode tests (error byte 02h)
|
||||
---------------------------------------------
|
||||
04Eh - absolute,X NOPs less than 3 bytes long
|
||||
04Fh - implied NOPs affects regs/flags
|
||||
050h - ZP,X NOPs less than 2 bytes long
|
||||
051h - absolute NOP less than 3 bytes long
|
||||
052h - ZP NOPs less than 2 bytes long
|
||||
053h - absolute,X NOPs less than 3 bytes long
|
||||
054h - implied NOPs affects regs/flags
|
||||
055h - ZP,X NOPs less than 2 bytes long
|
||||
056h - absolute NOP less than 3 bytes long
|
||||
057h - ZP NOPs less than 2 bytes long
|
||||
|
||||
LAX - "invalid" opcode tests
|
||||
----------------------------
|
||||
07Ch - LAX (indr,x) failure
|
||||
07Dh - LAX (indr,x) failure
|
||||
07Eh - LAX zeropage failure
|
||||
07Fh - LAX zeropage failure
|
||||
080h - LAX absolute failure
|
||||
081h - LAX absolute failure
|
||||
082h - LAX (indr),y failure
|
||||
083h - LAX (indr),y failure
|
||||
084h - LAX zp,y failure
|
||||
085h - LAX zp,y failure
|
||||
086h - LAX abs,y failure
|
||||
087h - LAX abs,y failure
|
||||
|
||||
SAX - "invalid" opcode tests
|
||||
----------------------------
|
||||
088h - SAX (indr,x) failure
|
||||
089h - SAX (indr,x) failure
|
||||
08Ah - SAX zeropage failure
|
||||
08Bh - SAX zeropage failure
|
||||
08Ch - SAX absolute failure
|
||||
08Dh - SAX absolute failure
|
||||
08Eh - SAX zp,y failure
|
||||
08Fh - SAX zp,y failure
|
||||
|
||||
SBC - "invalid" opcode test
|
||||
---------------------------
|
||||
090h - SBC failure
|
||||
091h - SBC failure
|
||||
092h - SBC failure
|
||||
093h - SBC failure
|
||||
094h - SBC failure
|
||||
|
||||
DCP - "invalid" opcode tests
|
||||
----------------------------
|
||||
095h - DCP (indr,x) failure
|
||||
096h - DCP (indr,x) failure
|
||||
097h - DCP (indr,x) failure
|
||||
098h - DCP zeropage failure
|
||||
099h - DCP zeropage failure
|
||||
09Ah - DCP zeropage failure
|
||||
09Bh - DCP absolute failure
|
||||
09Ch - DCP absolute failure
|
||||
09Dh - DCP absolute failure
|
||||
09Eh - DCP (indr),y failure
|
||||
09Fh - DCP (indr),y failure
|
||||
0A0h - DCP (indr),y failure
|
||||
0A1h - DCP zp,x failure
|
||||
0A2h - DCP zp,x failure
|
||||
0A3h - DCP zp,x failure
|
||||
0A4h - DCP abs,y failure
|
||||
0A5h - DCP abs,y failure
|
||||
0A6h - DCP abs,y failure
|
||||
0A7h - DCP abs,x failure
|
||||
0A8h - DCP abs,x failure
|
||||
0A9h - DCP abs,x failure
|
||||
|
||||
ISB - "invalid" opcode tests
|
||||
----------------------------
|
||||
0AAh - DCP (indr,x) failure
|
||||
0ABh - DCP (indr,x) failure
|
||||
0ACh - DCP (indr,x) failure
|
||||
0ADh - DCP zeropage failure
|
||||
0AEh - DCP zeropage failure
|
||||
0AFh - DCP zeropage failure
|
||||
0B0h - DCP absolute failure
|
||||
0B1h - DCP absolute failure
|
||||
0B2h - DCP absolute failure
|
||||
0B3h - DCP (indr),y failure
|
||||
0B4h - DCP (indr),y failure
|
||||
0B5h - DCP (indr),y failure
|
||||
0B6h - DCP zp,x failure
|
||||
0B7h - DCP zp,x failure
|
||||
0B8h - DCP zp,x failure
|
||||
0B9h - DCP abs,y failure
|
||||
0BAh - DCP abs,y failure
|
||||
0BBh - DCP abs,y failure
|
||||
0BCh - DCP abs,x failure
|
||||
0BDh - DCP abs,x failure
|
||||
0BEh - DCP abs,x failure
|
||||
|
||||
SLO - "invalid" opcode tests
|
||||
----------------------------
|
||||
0BFh - SLO (indr,x) failure
|
||||
0C0h - SLO (indr,x) failure
|
||||
0C1h - SLO (indr,x) failure
|
||||
0C2h - SLO zeropage failure
|
||||
0C3h - SLO zeropage failure
|
||||
0C4h - SLO zeropage failure
|
||||
0C5h - SLO absolute failure
|
||||
0C6h - SLO absolute failure
|
||||
0C7h - SLO absolute failure
|
||||
0C8h - SLO (indr),y failure
|
||||
0C9h - SLO (indr),y failure
|
||||
0CAh - SLO (indr),y failure
|
||||
0CBh - SLO zp,x failure
|
||||
0CCh - SLO zp,x failure
|
||||
0CDh - SLO zp,x failure
|
||||
0CEh - SLO abs,y failure
|
||||
0CFh - SLO abs,y failure
|
||||
0D0h - SLO abs,y failure
|
||||
0D1h - SLO abs,x failure
|
||||
0D2h - SLO abs,x failure
|
||||
0D3h - SLO abs,x failure
|
||||
|
||||
RLA - "invalid" opcode tests
|
||||
----------------------------
|
||||
0D4h - RLA (indr,x) failure
|
||||
0D5h - RLA (indr,x) failure
|
||||
0D6h - RLA (indr,x) failure
|
||||
0D7h - RLA zeropage failure
|
||||
0D8h - RLA zeropage failure
|
||||
0D9h - RLA zeropage failure
|
||||
0DAh - RLA absolute failure
|
||||
0DBh - RLA absolute failure
|
||||
0DCh - RLA absolute failure
|
||||
0DDh - RLA (indr),y failure
|
||||
0DEh - RLA (indr),y failure
|
||||
0DFh - RLA (indr),y failure
|
||||
0E0h - RLA zp,x failure
|
||||
0E1h - RLA zp,x failure
|
||||
0E2h - RLA zp,x failure
|
||||
0E3h - RLA abs,y failure
|
||||
0E4h - RLA abs,y failure
|
||||
0E5h - RLA abs,y failure
|
||||
0E6h - RLA abs,x failure
|
||||
0E7h - RLA abs,x failure
|
||||
0E8h - RLA abs,x failure
|
||||
|
||||
SRE - "invalid" opcode tests
|
||||
----------------------------
|
||||
0E8h - SRE (indr,x) failure
|
||||
0EAh - SRE (indr,x) failure
|
||||
0EBh - SRE (indr,x) failure
|
||||
0ECh - SRE zeropage failure
|
||||
0EDh - SRE zeropage failure
|
||||
0EEh - SRE zeropage failure
|
||||
0EFh - SRE absolute failure
|
||||
0F0h - SRE absolute failure
|
||||
0F1h - SRE absolute failure
|
||||
0F2h - SRE (indr),y failure
|
||||
0F3h - SRE (indr),y failure
|
||||
0F4h - SRE (indr),y failure
|
||||
0F5h - SRE zp,x failure
|
||||
0F6h - SRE zp,x failure
|
||||
0F7h - SRE zp,x failure
|
||||
0F8h - SRE abs,y failure
|
||||
0F9h - SRE abs,y failure
|
||||
0FAh - SRE abs,y failure
|
||||
0FBh - SRE abs,x failure
|
||||
0FCh - SRE abs,x failure
|
||||
0FDh - SRE abs,x failure
|
||||
|
||||
|
||||
RRA - "invalid" opcode tests
|
||||
----------------------------
|
||||
001h - RRA (indr,x) failure
|
||||
002h - RRA (indr,x) failure
|
||||
003h - RRA (indr,x) failure
|
||||
004h - RRA zeropage failure
|
||||
005h - RRA zeropage failure
|
||||
006h - RRA zeropage failure
|
||||
007h - RRA absolute failure
|
||||
008h - RRA absolute failure
|
||||
009h - RRA absolute failure
|
||||
00Ah - RRA (indr),y failure
|
||||
00Bh - RRA (indr),y failure
|
||||
00Ch - RRA (indr),y failure
|
||||
00Dh - RRA zp,x failure
|
||||
00Eh - RRA zp,x failure
|
||||
00Fh - RRA zp,x failure
|
||||
010h - RRA abs,y failure
|
||||
011h - RRA abs,y failure
|
||||
012h - RRA abs,y failure
|
||||
013h - RRA abs,x failure
|
||||
014h - RRA abs,x failure
|
||||
015h - RRA abs,x failure
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
001h -
|
||||
002h -
|
||||
003h -
|
||||
004h -
|
||||
005h -
|
||||
006h -
|
||||
007h -
|
||||
008h -
|
||||
009h -
|
||||
00Ah -
|
||||
00Bh -
|
||||
00Ch -
|
||||
00Dh -
|
||||
00Eh -
|
||||
00Fh -
|
||||
010h -
|
||||
|
||||
|
||||
Todo: check to see if decimal mode is missing on CPU
|
||||
BIN
data/testsuite/nestress.nes
Normal file
BIN
data/testsuite/nestress.nes
Normal file
Binary file not shown.
1106
docs/nesdoc1.txt
Normal file
1106
docs/nesdoc1.txt
Normal file
File diff suppressed because it is too large
Load Diff
865
docs/nesdoc2.txt
Normal file
865
docs/nesdoc2.txt
Normal file
@@ -0,0 +1,865 @@
|
||||
|
||||
Vous êtes déjà arrivé jusqu'ici?? Wow mes féliciations!!
|
||||
Maintenant ça risque de devenir un peu plus corsé...
|
||||
|
||||
|
||||
Fonctionnement de la PPU (Picture Processing Unit):
|
||||
--------------------------------------------------
|
||||
|
||||
Petite intro:
|
||||
|
||||
Comprendre le fonctionnement de la PPU c'est un peu une manière de comprendre
|
||||
comment une console fonctionne, à savoir décharger le maximum de tâches à
|
||||
ses coprocesseurs. Dans le cas présent de toute façon la NES a un fonctionnement
|
||||
extrèmement simple puisque la PPU est le seul véritable coprocesseur.
|
||||
|
||||
1. Partage de la VRAM:
|
||||
|
||||
La NES dispose de 16 ko de mémoire video (et pas 32 ko comme on peut le voir souvent).
|
||||
D'ailleurs n'essayez absolument pas de donner au registre d'adresse de la PPU
|
||||
une adresse sur plus de 14 bits, car les $C000 octets suivants sont juste des
|
||||
miroirs.
|
||||
Cette mémoire est complétement dédiée au fonctionnement de la PPU, et n'est pas
|
||||
directement accessible par le processeur central. Par conséquent, pour pouvoir
|
||||
accéder à cette mémoire, on dispose de toute une batterie de pseudo-registres qui
|
||||
sont en fait utilisés tels des adresses en mémoire centrale. Ces pseudo-registres
|
||||
permettent en fait de piloter la PPU pour l'affichage des sprites, faire des scrollings,
|
||||
régler les couleurs, ect...
|
||||
|
||||
Voilà comment est partagée la mémoire:
|
||||
$0 -> $FFF : Table des graphismes #0 (Pattern Table) d'une taille de $1000 (4096)
|
||||
$1000 -> $1FFF : Table des graphismes #1 d'une taille de $1000 (4096)
|
||||
-> Remarque: si la rom à un mapper particulier (défini dans l'header),
|
||||
il y a moyen que ces tables de graphismes soient en fait 2 banques de la rom (CHR-ROM),
|
||||
ou plus, ce sera expliqué plus loin avec l'utilisation du Multi-Memory Controller.
|
||||
Quoi qu'il en soit ca n'affectera que les $2000 premiers octets de la VRAM.
|
||||
$2000 -> $23BF : Table de nommage #0 (Name Table) d'une taille de $3C0 (960)
|
||||
$23C0 -> $23FF : Table d'attribution #0 (Attribute Table) d'une taille de $40 (64)
|
||||
$2400 -> $27BF : Table de nommage #1 d'une taille de $3C0
|
||||
$27C0 -> $27FF : Table d'attribution #1 d'une taille de $40
|
||||
$2800 -> $2BBF : Table de nommage #2 d'une taille de $3C0
|
||||
$2BC0 -> $2BFF : Table d'attribution #2 d'une taille de $40
|
||||
$2C00 -> $2FBF : Table de nommage #3 d'une taille de $3C0
|
||||
$2FC0 -> $2FFF : Table d'attribution #3 d'une taille de $40
|
||||
$3000 -> $3EFF : Non utilisé
|
||||
$3F00 -> $3F0F : Palette des images d'une taille de $10 (16)
|
||||
$3F10 -> $3F1F : Palette des sprites d'une taille de $10
|
||||
$3F20 -> $3FFF : Miroir des palettes d'une taille de $EO (224)
|
||||
|
||||
Au total $4000 octets soit 16384 octets.
|
||||
|
||||
Quelques détails au niveau des miroirs utilisés dans la VRAM:
|
||||
- les tables de nommage sont soumis à un effet de miroir, qui sera expliqué plus loin.
|
||||
- même chose pour les palettes.
|
||||
|
||||
2. Tables des graphismes:
|
||||
|
||||
Il s'agit en fait d'abord d'expliquer comment sont stockées les données graphiques en
|
||||
mémoire video. Les données sont organisées en "tiles", de 8x8 pixels.
|
||||
Pour chaque tile les couleurs sont codées sur 2 bits, ainsi pour chaque tile on a
|
||||
besoin de $10 octets (16).
|
||||
On se sert des 8 premiers octets pour code le premier bit de chaque pixel et du second
|
||||
pour le deuxieme bit. Voilà ce que ça donne plus concrètement:
|
||||
|
||||
Premier bit Deuxième bit Représentation de la tile
|
||||
|
||||
1. #00011000b -> $18 9. #10000001b -> $81 2 . . 1 1 . . 2
|
||||
2. #01000010b -> $42 10. #00000000b -> $00 . 1 . . . . 1 .
|
||||
3. #10100101b -> $A5 11. #00100100b -> $24 1 . 3 . . 3 . 1
|
||||
4. #10000001b -> $81 12. #00000000b -> $00 1 . . . . . . 1
|
||||
5. #01000010b -> $42 13. #00011000b -> $18 . 1 . 2 2 . 1 .
|
||||
6. #01000010b -> $42 14. #00000000b -> $00 . 1 . . . . 1 .
|
||||
7. #00111100b -> $3C 15. #00000000b -> $00 . . 1 1 1 1 . .
|
||||
8. #00000000b -> $00 16. #00011000b -> $18 . . . 2 2 . . .
|
||||
|
||||
Donc la VRAM contient 2 tables des graphismes pouvant chacune contenir 256 tiles (4096 octets).
|
||||
Soit un total de 512 tiles stockables possibles en mémoire.
|
||||
Pour pouvoir les afficher on va avoir recours aux tables de nommage.
|
||||
|
||||
3. Tables de nommage:
|
||||
|
||||
Les tables de nommage sont en fait des matrices de tiles, et vont constituer la base de l'affichage.
|
||||
La NES a en effet un affichage de 32*30 tiles (soit en pixels 256*240).
|
||||
Donc 32*30=960 soit la mémoire disponible pour une table de nommage. Chaque octet codé dans la table de nommage
|
||||
sera un index vers une tile dans la table des graphismes.
|
||||
Remarque: sur la version NTSC de la NES, les 8 premières et dernières lignes ne sont pas affichées.
|
||||
|
||||
4. Tables d'attribution:
|
||||
|
||||
Le rôle des tables d'attribution est complémentaire à celui des tables de nommage.
|
||||
Comme vu précedemment les couleurs des tiles sont codées sur 2 bits. Mais le système de couleur
|
||||
de la PPU permet un affichage en 16 couleurs, donc codage 4 bits.
|
||||
Pour pouvoir accéder à ces 16 couleurs on a recours aux tables d'attribution.
|
||||
Voilà comment ces tables fonctionnent:
|
||||
|
||||
-Chaque octet dans la table d'attribution retient les 2 bits de poids fort de chaque couleur
|
||||
dans un groupe 4*4 tiles. Pour un groupe de 4 tiles on a ainsi 2 bits dans un octet de la table d'attribution.
|
||||
On peut controler cette equivalence:
|
||||
taille de la table d'attribution -> 64 octets
|
||||
nombre de tiles stockables dans une table de nommage -> 960
|
||||
|
||||
Cela donne shématiquement:
|
||||
octet de la table d'attribution #0
|
||||
33221100
|
||||
||||||+--------- tiles #0 à #3
|
||||
||||+----------- tiles #4 à #7
|
||||
||+------------- tiles #8 à #B
|
||||
+--------------- tiles #C à #F
|
||||
|
||||
Remarque: le numero de tile #? ne correspond pas à l'index de la tile dans la table de nommage
|
||||
car en réalité les couleurs ne sont pas attribuées séquentiellement mais par blocs de 4 tiles.
|
||||
|
||||
|
||||
Complété avec les 2 bits de poids faibles situés dans la table des graphismes on obtient la couleur de la palette qui
|
||||
sera alors affichée à l'écran.
|
||||
|
||||
5. Fonctionnement des palettes:
|
||||
|
||||
La PPU utilise 2 palettes :
|
||||
-la palette des images déstinées à l'affichage des objets statiques d'arrière plan.
|
||||
-la palette des sprites déstinée comme son nom l'indique à l'affichage des sprites.
|
||||
|
||||
Chacune des palette à une taille de 16 octets. Il ne s'agit pas de réelles valeurs RGB
|
||||
mais une référence à une palette interne d'une variété de 52 couleurs possibles.
|
||||
D'autre part les palettes ne sont pas réellement de 16 octets, car les adresses $3F00,
|
||||
$3F04, $3F08 ect... tous les 4 octets renvois en fait à la même couleur!
|
||||
Une couleurs très particulière car c'est elle qui va définir la transparence.
|
||||
Ce qui limite la palette au nombre de couleurs effectif de 13 couleurs
|
||||
Comme on dispose de 13 couleurs en réalité on peut en afficher jusqu'a 25 (une couleur étant commune aux 2 palettes)
|
||||
à l'ecran en même temps (fond d'écran + sprites)
|
||||
|
||||
Pour chaque octet de la palette voilà comment proceder:
|
||||
uullhhhh
|
||||
||||||||
|
||||
||||+----- 4 premiers bits -> défini la valeur chromatique de la couleur:
|
||||
|||| -> attention cette valeur doit être comprise entre 1 et C (12) (sinon -> couleur noire)
|
||||
|||| -> mettre 0 permet d'afficher une nuance de gris selon la luminance choisie
|
||||
||+------- ces 2 bits définissent la luminance, niveau de luminosité choisi
|
||||
+--------- ces derniers bits ne sont pas utilisés
|
||||
|
||||
Voilà les codes correspondant aux couleurs pour les valeur chromatiques:
|
||||
$00 = gris $05 = rose $0A = vert
|
||||
$01 = bleu clair $06 = rouge $0B = vert foncé
|
||||
$02 = bleu cyan $07 = ocre $0C = bleu ardoise
|
||||
$03 = bleu foncé $08 = marron
|
||||
$04 = violet $09 = vert clair
|
||||
Les niveaux de luminosité peuvent semblibement faire varier ces couleurs
|
||||
Il existe un très bon tableau pour voir chaque couleur associée à chaque code sur ce site:
|
||||
http://www.sfu.ca/~ccovell/NESTechFAQ.html
|
||||
|
||||
6. Fonctionnement des miroirs:
|
||||
|
||||
En réalité la VRAM de la NES dispose d'assez de mémoire pour seulement 2 tables de nommage et 2 tables d'attribution.
|
||||
Il en résulte que 2 tables sont en fait des "miroirs" des 2 autres tables.
|
||||
Chaque entrée dans une table est automatiquement alors faites dans l'autre.
|
||||
|
||||
On dispose de 3 modes pour les miroirs (ils sont définis dans l'header pour une rom) :
|
||||
-le premier mode désactive simplement cet effet, et autorise l'utilisation d'une seule table de nommage (et d'attribution).
|
||||
-le second mode, le mode "horizontal", permet d'utiliser les tables #0 et #2, la table #1 est un miroir de la table #0
|
||||
et la table #3 est un miroir de la table #2.
|
||||
-le dernier mode, le mode "vertical", permet d'utiliser les tables #0 et #1, la table #2 est un miroir de la table #0
|
||||
et la table #3 est un miroir de la table #1.
|
||||
|
||||
Les palettes aussi disposent de miroirs, et chaque intervalle à partir de l'adresse $3F20 jusqu'a $3FFF sont des miroirs
|
||||
respectifs des 2 palettes tous les $10 octets (16).
|
||||
|
||||
7. Scrolling de l'arrière plan:
|
||||
|
||||
Comme toute bonne console la NES dispose d'une fonction de scrolling cablée en hardware, ce qui permet un scrolling
|
||||
irréprochable, contrairement à ce que l'on peut obtenir sur un PC.
|
||||
Etant donnée qu'une table de nommage équivaut à la taille d'un ecran, il va falloir utiliser la deuxieme table de
|
||||
nommage à notre disposition.
|
||||
On peut faire scroller l'arrière plan sur 2 axes seulement: horizontalement et verticalement.
|
||||
La résolution est de toute facon de 256*240.
|
||||
Voila shématiquement ce que l'on obtient:
|
||||
|
||||
Horizontal: Vertical:
|
||||
0 512 -> 256*2 +---------+0
|
||||
+---------+---------+ | |
|
||||
| | | | A |
|
||||
| A | B | | |
|
||||
| | | +---------+
|
||||
| | | | |
|
||||
+---------+---------+ | B |
|
||||
| |
|
||||
+---------+480 -> 240*2
|
||||
|
||||
Pour définir quelle table de nommage on utilise pour "A" on a recours aux bits 0 et 1 du registre $2000 qui sera expliqué
|
||||
plus loin. Logiquement "B" sera l'autre table de nommage.
|
||||
Le type de miroir utilisé est défini dans l'header:
|
||||
Horizontal: Table #0 -> Miroir Table #1
|
||||
Table #2 -> Miroir Table #3
|
||||
Vertical: Table #0 -> Miroir Table #2
|
||||
Table #1 -> Miroir Table #3
|
||||
|
||||
Le shéma pour le scrolling des tables est le suivant:
|
||||
|
||||
Table de nommage #2 Table de nommage #3
|
||||
|
||||
|
||||
Table de nommage #0 Table de nommage #1
|
||||
|
||||
L'utilisation du scrolling s'effectue par l'écriture successive de valeurs 8 bits dans le registre $2005.
|
||||
La première valeur ecrite correspond au scrolling vertical, par conséquent elle doit être impérativement
|
||||
inférieure ou égale 239 (du fait de la limitation de la résolution de la NES).
|
||||
La seconde valeur écrite juste après correspond au scrolling horizontal.
|
||||
|
||||
8. Fonctionnement des sprites:
|
||||
|
||||
La PPU dispose de capacités particulières pour la gestion des sprites. Cela va permettre de simplifier l'affichage
|
||||
de sprites à l'écran puisqu'ils seront complètement independant de l'arrière plan sur lequel ils vont apparaitre.
|
||||
|
||||
Il y a une mémoire incluse permettant de stocker les informations de 64 sprites, qui peuvent être d'une taille de 8*8
|
||||
ou 8*16 pixels (8->largeur, 16->longueur). Les données des sprites sont contenues dans la table des graphismes.
|
||||
Pour permettre le stockage de ces informations la NES dispose d'un espace mémoire complétement indépendant de la
|
||||
VRAM et de la mémoire centrale. Cette mémoire est de 256 octets, à raison de 4 octets par sprite (donc capacité pour 64).
|
||||
|
||||
-----------------------------------------------------
|
||||
Pour un sprite:
|
||||
-Octet 0 -> Coordonnée Y du point où le sprite se trouve, c'est à dire l'ordonnée.
|
||||
Ce point est le point du coin haut-gauche du sprite:
|
||||
|
||||
point ->+-------+
|
||||
| |
|
||||
| |
|
||||
| |
|
||||
+-------+
|
||||
|
||||
-Octet 1 -> Il s'agit de l'index dans la table des graphismes des données du sprite.
|
||||
S'utilise de la même façon que la table de nommage.
|
||||
Attention les sprites en 8*16 ont une particularité -> voir plus bas.
|
||||
|
||||
-Octet 2 -> Cet octet contient une série de flags qu'il convient d'expliciter:
|
||||
vhp000cc -> octet divisé par flags
|
||||
|
||||
flag v: Flip Vertical -> mis à 1 active le flip vertical (mouvement rapide), sinon désactivé.
|
||||
flag h: Flip Horizontal -> même chose horizontalement.
|
||||
flag p: Priorité d'arrière-plan -> mis à 1 si le sprite doit être caché derrière, sinon laisser à 0.
|
||||
flag 0: Inutilisé
|
||||
flag c: Les 2 bits de poids forts de la couleur, car on ne dispose pas ici des tables d'attributions...
|
||||
Ce flag fonctionne comme les tables d'attribution (voir précédemment).
|
||||
|
||||
-Octet 3 -> Coordonnée X du point où le sprite se trouve, c'est à dire l'abcisse.
|
||||
|
||||
-----------------------------------------------------
|
||||
|
||||
L'utilisation des sprites d'une taille de 8*16 est différente car leur taille fait le double d'une tile normale (8*8).
|
||||
Par conséquent l'adressage par le biais de l'octet d'index dans une valeur de sprite sera interprété différement
|
||||
et on accedera ainsi soit à la première table des graphismes (Pattern #0 à l'adresse en VRAM $0000) ou bien
|
||||
à la seconde table des graphismes (Pattern #1 à l'adresse en VRAM $1000).
|
||||
|
||||
On peut uniquement afficher 8 sprites à la fois sur une "ligne" (scanline), c'est à dire si les sprites ont
|
||||
les mêmes coordonnées en abcisse(X). Techniquement la PPU peut cependant difficilement gérer plus de sprites que cela.
|
||||
|
||||
9. VBlank et HBlank:
|
||||
|
||||
VBlank est pour Vertical Blanking, et HBlank pour Horizontal Blanking. Le blanking c'est le scintillement emis par
|
||||
chaque projection de pixels par le cannon à electrons sur l'écran. La PPU a comme chaque périphérique d'affichage de
|
||||
console un taux de rafraichissement. Ce taux est de 60 hertz (60x/secondes) pour une NES NTSC et 50 hertz pour une PAL.
|
||||
|
||||
Shématiquement le cannon à élecron va ecrire sur la TV les lignes de gauche à droite et une fois arrivé au bout de
|
||||
la ligne il doit revenir à gauche pour ecrire la ligne suivante -> cette période est appelée HBlank.
|
||||
|
||||
Une fois tout l'écran complété il doit revenir tout en haut de l'écran pour procéder à l'affichage de l'écran suivant.
|
||||
Cette période est appelé VBlank.
|
||||
|
||||
On peut voir si la PPU est en période de VBlank en controlant le bit #7 du registre $2002(il est alors à 1), tout comme
|
||||
on peut mettre celui ci à zero. Celui ci est remis à 0 à la fin du VBlank.
|
||||
|
||||
Le bit #6 du registre $2002 est appelé Hit Flag, et sert a controler à l'affichage d'un sprite quand
|
||||
est-ce que l'on va ecrire la première ligne du sprite (il sera alors mis à 1). Si la coordonnée en ordonnée (Y)
|
||||
du sprite est 12, ce flag sera mis à 1 lors de l'écriture de la ligne 12. Il est automatiquement remis à 0
|
||||
lors du VBlank.
|
||||
|
||||
On utilise souvent le contrôle du VBlank comme un timer, à défaut d'y avoir une quelquonque horloge sur la NES.
|
||||
|
||||
Fonctionnement des autres peripheriques:
|
||||
---------------------------------------
|
||||
|
||||
1. Pseudo-Audio Processing Unit:
|
||||
|
||||
Comme son nom l'indique la Pseudo-Audio Processing Unit (pAPU) permet à la NES de sortir du son.
|
||||
Le "Pseudo" vient du fait qu'en réalité il n'existe pas de véritable composant indépendant ayant en charge
|
||||
le traitement du son (à la différence de la SUPERNES et de son SPC700).
|
||||
Le son est en fait traité directement par le processeur et une série de composants de base, les synthèses
|
||||
étant transmises par des registres commandés directement depuis le processeur.
|
||||
|
||||
La NES dispose de 4 voix de synthèses FM et une voix pour le son digital (PCM).
|
||||
Parmi les voies de synthèses, on compte 2 voix de rythme (pulse), une voix de triangle, et
|
||||
un canal de bruit.
|
||||
|
||||
Les voix de rythme ont la possibilité de produire du son dans les fréquences de 54,6 Hz à 12,4 Khz.
|
||||
Il y a possibilité de régler la fréquence de sifflement et la durée du cycle.
|
||||
|
||||
La voix de triangle peut produire du son entre les fréquences 27.3 Hz et 55.9 Khz dans une
|
||||
résolution de 4-bits (donc 16 tonalitées différentes). Ce canal dispose d'un compteur linéaire
|
||||
ce qui permet de le désactiver après une certaine période d'utilisation.
|
||||
|
||||
Le canal de bruit lui a la faculté de produire des sons à des fréquences aléatoires entre 29.3
|
||||
Hz et 447 KHz. On peut l'utiliser pour produire des nombres aléatoires en sortie du canal (utile
|
||||
pour les jeux).
|
||||
|
||||
Fonctionnement du canal digital (DMC):
|
||||
Le canal DMC répond à un fonctionnement bien particulier en comparaison des autres canaux sonores de la pAPU.
|
||||
|
||||
3. Joypads
|
||||
|
||||
Je ne m'interesserais ici qu'au fonctionnement des 2 joypads de base. Les autres périphériques pourraient
|
||||
éventuellement être interessants au cas où vous travaillerez sur un émulateur, auquel cas le contenu de
|
||||
cette documentation se révelerait bien insuffisante.
|
||||
|
||||
La NES supporte donc 2 joypads à la fois (ou un joypad et le Zapper, le pistolet à laser).
|
||||
Ces 2 joypads correspondent respectivement aux registres $4016 et $4017. Le fonctionnement
|
||||
des joypads est particulier en comparaison des autres registres de périphérique.
|
||||
Pour pouvoir utiliser ces registres ont doit d'abord les remettre à l'état initial
|
||||
en écrivant successivement 1 puis 0 dans le registre adéquate.
|
||||
Ensuite on doit réalisé le nombre de lecture nécessaire pour accéder à la touche
|
||||
qui nous interesse de vérifier qu'elle a été préssé.
|
||||
Nombre de lectures défini comme suit:
|
||||
|
||||
Pour chaque registre (soit $4016 ou $4017):
|
||||
$4016:
|
||||
|
||||
Joypad #1 Joypad #3 (*)
|
||||
1 -> A 9 -> A 17 ->----+
|
||||
2 -> B 10 -> B 18 -> |___ ces 4 valeurs sont utilisées pour
|
||||
3 -> SELECT 11 -> SELECT 19 -> | définir quel manette est connectée ("signature")
|
||||
4 -> START 12 -> START 20 ->----+
|
||||
5 -> HAUT 13 -> HAUT 21 -> 0
|
||||
6 -> BAS 14 -> BAS 22 -> 0
|
||||
7 -> GAUCHE 15 -> GAUCHE 23 -> 0
|
||||
8 -> DROITE 16 -> DROITE 24 -> 0
|
||||
|
||||
* -> le joypad #3 est ici uniquement possible si on utilise l'adaptateur 4 manettes.
|
||||
Pareil pour le registre $4017, le Joypad #1 équivaut au Joypad #2
|
||||
" " " " , le Joypad #3 équivaut au Joypad #4
|
||||
Si on utilise pas l'adaptateur 4 manettes les valeurs 9 à 16 sont inutiles.
|
||||
|
||||
Imaginons que nous devions voir si la touche SELECT a été préssée sur le joypad #2,
|
||||
on aura alors le code assembleur suivant:
|
||||
|
||||
LDA #$01 --|
|
||||
STA $4017 |---> remise à 0 du port #2
|
||||
LDA #$00 --|
|
||||
STA $4017
|
||||
LDA $4017 --|
|
||||
LDA $4017 |---> 3 lectures pour SELECT
|
||||
LDA $4017 --|
|
||||
AND #$01 ------> on ne récupère que le bit #0 (voir details dans "Détails sur les registres de periphériques")
|
||||
|
||||
Si la valeur contenue dans l'accumulateur est à 1 alors la touche SELECT a été cochée.
|
||||
|
||||
Signature:
|
||||
On peut vérifier en effectuant un certain nombre de lecture (17)
|
||||
le nombre de joypads connectés. Ainsi on procède comme suit:
|
||||
%0000 -> aucun joypad ou périphérique connecté
|
||||
%0001 -> joypad connecté au port #1 ($4016)
|
||||
#0010 -> joypad connecté au port #2 ($4017)
|
||||
Remarque: les 4 bits correspondent aux valeurs de 17 à 20.
|
||||
On obtient cette valeur après 17 lectures dans le registre $4016 ou $4017
|
||||
|
||||
C'est a peu près tout ce qu'il faut savoir pour les joypads. D'autres types de périphériques d'entrée ont
|
||||
un fonctionnement légèrement différent cependant ils utilisent quoi qu'il arrive les ports $4016 et $4017.
|
||||
|
||||
|
||||
Détails sur les registres de periphériques:
|
||||
------------------------------------------
|
||||
|
||||
On a pas directement accès aux périphériques comme la PPU ou bien les joypads, il faut trouver en moyen à partir de la
|
||||
programmation du processeur central pour piloter ces périphériques. Pour cela on utilise une série de pseudo-registres
|
||||
qui sont en fait des adresses mémoire. Ces adresses sont données en 16 bits.
|
||||
Selon le périphérique on va devoir ecrire (cas de la PPU) ou bien lire (généralement pour la pAPU).
|
||||
On va aussi devoir parfois proceder a une ecriture successive de 2 valeurs.
|
||||
|
||||
|
||||
1. Registres PPU:
|
||||
|
||||
|
||||
+--------------------------------------------------------------------------------------------------------+
|
||||
|Registre $2000: Registre de contrôle de la PPU #1 (Ecriture seulement)
|
||||
| masque: vmsbpiNN
|
||||
|
|
||||
| v = Mis à 1 ce bit éxecute automatique une routine NMI (Non Maskable Interrupt) lors d'un VBlank
|
||||
| Rappel: l'adresse de cette routine se trouve en $FFFA dans la PRG-ROM.
|
||||
|
|
||||
| m = Selection PPU
|
||||
| Positionné à Maitre à 0, Esclave à 1. (inutilisé apparament)
|
||||
|
|
||||
| s = Taille des sprites
|
||||
| Si le bit est à 0 -> sprites en 8x8 (taille standard d'une tile)
|
||||
| " " " -> sprites en 8x16
|
||||
|
|
||||
| b = Adresse de la table des graphismes utilisée pour l'arrière plan en VRAM
|
||||
| 0 = $0000
|
||||
| 1 = $1000
|
||||
|
|
||||
| p = Adresse de la table des graphismes utilisée pour les sprites en VRAM
|
||||
| 0 = $0000
|
||||
| 1 = $1000
|
||||
|
|
||||
| i = Incrémentation d'adresse PPU
|
||||
| 0 = incrémentation de 1
|
||||
| 1 = incrémentation de 32
|
||||
|
|
||||
| NN = Adresse de la table de nommage (Name Table) utilisée en VRAM
|
||||
| 00 = $2000
|
||||
| 01 = $2400
|
||||
| 10 = $2800
|
||||
| 11 = $2C00
|
||||
+--------------------------------------------------------------------------------------------------------+
|
||||
|Registre $2001: Registre de contrôle de la PPU (Ecriture seulement)
|
||||
| masque: fffvVsbt
|
||||
|
|
||||
| fff = Couleur de teint utilisée par defaut ("Colour Emphasis")
|
||||
| 000 = Noir (pas de teint)
|
||||
| 001 = Rouge
|
||||
| 010 = Bleu
|
||||
| 100 = Vert
|
||||
| Remarque: ce n'est pas encore correctement émulé sur certains émulateurs.
|
||||
|
|
||||
| v = Visibilité des sprites (mettre à 1 par défaut pour pouvoir afficher des sprites)
|
||||
|
|
||||
| V = Visibilité de l'arrière plan (mettre à 1 par défaut pour pouvoir afficher l'arrière plan)
|
||||
|
|
||||
| s = Clipping des sprites
|
||||
| 0 = les sprites ne sont pas affichés sur les 8 pixels gauches
|
||||
| 1 = pas de clipping
|
||||
|
|
||||
| b = Clipping de l'arrière-plan
|
||||
| 0 = l'arrière-plan n'est pas affiché sur les 8 pixels gauches
|
||||
| 1 = pas de clipping
|
||||
|
|
||||
| t = Type d'affichage
|
||||
| 0 = affichage couleur
|
||||
| 1 = affichage noir et blanc
|
||||
+--------------------------------------------------------------------------------------------------------+
|
||||
|Registre $2002: Registre de Status de la PPU (Lecture seulement)
|
||||
| masque: vhsw---- (-> 4 bits de poids faibles non utilisés)
|
||||
|
|
||||
| v = Période de VBlank
|
||||
| Ce bit est mis à 1 durant une période de VBlank.
|
||||
| Il est mis à 0 dès qu'on fait une lecture de ce registre pour
|
||||
| éviter de reéxecuter une routine dans un même VBlank.
|
||||
| Remarque: très utile pour s'en servir comme timer.
|
||||
|
|
||||
| h = Occurence Sprite #0
|
||||
| Ce bit est mis à 1 dès que le VBlank à atteint la position
|
||||
| du sprite #0 (du moins le premier pixel non transparent).
|
||||
| Il est mis à 0 après chaque VBlank.
|
||||
| Voir l'excellente FAQ de Chris Covell pour en savoir d'avantage.
|
||||
|
|
||||
| s = Compteur de sprites par ligne
|
||||
| 0 = 8 ou moins de 8 sprites par ligne
|
||||
| 1 = plus de 8 sprites par ligne
|
||||
|
|
||||
| w = Flag d'ecriture en VRAM
|
||||
| Ce bit mis à 1 indique que la PPU ignore les ecritures faites en VRAM.
|
||||
|
|
||||
| Remarque: Utilisez ce registre SYSTEMATIQUEMENT pour ecrire des données en VRAM.
|
||||
| Il n'est pas conseillé d'ecrire des données hors d'un VBlank durant l'execution.
|
||||
+--------------------------------------------------------------------------------------------------------+
|
||||
|Registre $2003: Registre d'adresse en SPR-RAM (Ecriture seulement)
|
||||
|
|
||||
| Permet d'écrire l'adresse 8 bits utilisée par le registre $2004
|
||||
| pour accéder à une case mémoire en SPR-RAM.
|
||||
+--------------------------------------------------------------------------------------------------------+
|
||||
|Registre $2004: Registre d'entrée-sortie en SPR-RAM (Lecture-Ecriture)
|
||||
|
|
||||
| Permet d'écrire ou de lire la case d'adresse définie par le registre $2003.
|
||||
+--------------------------------------------------------------------------------------------------------+
|
||||
|Registre $2005: Registre de Scrolling d'arrière-plan (Ecriture seulement)
|
||||
|
|
||||
| L'utilisation de ce registre se fait par 2 écritures successives.
|
||||
|
|
||||
| 1ère valeur: Scrolling horizontal
|
||||
| 2ème valeur: Scrolling vertical
|
||||
| Voir la partie sur les scrollings pour plus d'informations
|
||||
+--------------------------------------------------------------------------------------------------------+
|
||||
|Registre $2006: Registre d'adresse VRAM (Ecriture seulement)
|
||||
|
|
||||
| Il s'agit de l'adresse en VRAM que va utiliser le registre $2007.
|
||||
| L'écriture dans ce registre doit s'effectuer par 2 écritures successives
|
||||
| étant donné la limitation des bus à 8 bits encore une fois.
|
||||
|
|
||||
| 1ère valeur: 8 bits de poids fort de l'adresse
|
||||
| 2ème valeur: 8 bits de poids faible de l'adresse
|
||||
|
|
||||
| REMARQUE: Attention à l'incrémentation d'adresse PPU! A chaque ecriture dans le registre
|
||||
| $2007, l'adresse du registre $2006 est incrémentée soit de 1 ou de 32 (selon le registre PPU #1)
|
||||
+--------------------------------------------------------------------------------------------------------+
|
||||
|Registre $2007: Registre d'entrée-sortie en VRAM (Lecture-Ecriture)
|
||||
|
|
||||
| C'est le registre d'accès à la PPU le plus utilisé.
|
||||
| C'est par celui ce que vont passer toutes les ecritures pour les tables des graphismes,
|
||||
| de nommage ou d'attribution, bref toutes les données contenues en VRAM.
|
||||
| Il s'utilise de paire avec le registre $2006 qui défini l'offset en VRAM.
|
||||
+--------------------------------------------------------------------------------------------------------+
|
||||
|
||||
|
||||
Gardez à l'esprit que tous les transferts sont efféctués en 8 bits, ce qui inclue de nombreuses boucles si vous
|
||||
avez des données conséquentes à copier. Ainsi pour la SPR-RAM une autre solution gagne en facilité, expliqué
|
||||
juste en dessous.
|
||||
|
||||
Registre spécial:
|
||||
Pour accéder à la SPR-RAM on dispose d'une autre alternative qui facilite grandement les transferts car il
|
||||
permet de copier un bloc fixe de 256 octets de données qu'on aurait par exemple stocké en RAM ou en PRG-ROM.
|
||||
Il s'agit d'un registre DMA (Direct Memory Access) semblabe à ceux utilisés pour le son digital.
|
||||
Ce registre est à l'adresse $4014.
|
||||
La formule est simple:
|
||||
$100 * (opérande stockée dans le registre $4014) = adresse de début du bloc de 256 octets ($100)
|
||||
|
||||
Il est conseillé d'utiliser ce registre pour améliorer les performances du programme.
|
||||
|
||||
2. Registres pAPU
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
+--------------------------------------------------------------------------------------------------------+
|
||||
|Registre $4000: Registre de contrôle #1 de la voix de rythme #1 (Ecriture seulement)
|
||||
|
|
||||
| masque:%ccvessss
|
||||
| ||||+---- Taux d'echantillonage en lecture
|
||||
| |||+----- Selection d'envellope (0 = variée, 1 = fixée)
|
||||
| ||+------ Vague maintenue: répétion automatique (0 = non, 1 = oui)
|
||||
| +-------- Accomplissement du cycle
|
||||
| 00 = 87,5 %
|
||||
| 00 = 75,0 %
|
||||
| 00 = 50,0 %
|
||||
| 00 = 25,0 %
|
||||
+--------------------------------------------------------------------------------------------------------+
|
||||
|Registre $4001: Registre de contrôle #2 de la voix de rythme #1 (Ecriture seulement)
|
||||
|
|
||||
| masque:%fsssmrrr
|
||||
| |||||+--- étalement de la fréquence: 0 est le minimum, 7 le maximum
|
||||
| ||||| Remarque: voir selon bit #3 pour basse ou haute fréquence
|
||||
| ||||+---- méthode de fréquence (0 -> de haut vers bas, 1-> de bas vers haut)
|
||||
| |+------- vitesse de changement de fréquence
|
||||
| +-------- fréquence de sifflement
|
||||
| 0 -> les bits #0 à #6 sont ignorés
|
||||
| 1 -> les bits #0 à #6 sont pris en compte
|
||||
|
|
||||
+--------------------------------------------------------------------------------------------------------+
|
||||
|Registre $4002: Registre de fréquence #1 de la voix de rythme #1 (Ecriture seulement)
|
||||
|
|
||||
| Note: la fréquence des voix de rythme sont étalées sur 11 bits.
|
||||
| Ce registre permet de déterminer la valeur des 8 bits de
|
||||
| poids faible de la fréquence.
|
||||
|
|
||||
| masque:%ffffffff -> 8 bits de poids faible de la fréquence
|
||||
|
|
||||
+--------------------------------------------------------------------------------------------------------+
|
||||
|Registre $4003: Registre de fréquence #2 de la voix de rythme #1 (Ecriture seulement)
|
||||
|
|
||||
| masque: %tttttfff
|
||||
| |||||+--- les 3 bits de poids fort de la fréquence de la voix de rythme #2
|
||||
| +-------- temps d'activité de la voix
|
||||
+--------------------------------------------------------------------------------------------------------+
|
||||
|Registre $4004: Registre de contrôle #1 de la voix de rythme #2 (Ecriture seulement)
|
||||
|
|
||||
| Son fonctionnement est identique à la voix de rythme #1 (voir registre $4000)
|
||||
|
|
||||
+--------------------------------------------------------------------------------------------------------+
|
||||
|Registre $4005: Registre de contrôle #2 de la voix de rythme #2 (Ecriture seulement)
|
||||
|
|
||||
| Son fonctionnement est identique à la voix de rythme #1 (voir registre $4001)
|
||||
|
|
||||
+--------------------------------------------------------------------------------------------------------+
|
||||
|Registre $4006: Registre de fréquence #1 de la voix de rythme #2 (Ecriture seulement)
|
||||
|
|
||||
| Son fonctionnement est identique à la voix de rythme #1 (voir registre $4002)
|
||||
|
|
||||
+--------------------------------------------------------------------------------------------------------+
|
||||
|Registre $4007: Registre de fréquence #2 de la voix de rythme #2 (Ecriture seulement)
|
||||
|
|
||||
| Son fonctionnement est identique à la voix de rythme #1 (voir registre $4003)
|
||||
|
|
||||
+--------------------------------------------------------------------------------------------------------+
|
||||
|Registre $4008: Registre de contrôle #1 de la voix de triangle (Ecriture seulement)
|
||||
|
|
||||
| masque:%vccccccc
|
||||
| |+------- compteur linéaire de chargement du registre (voir précision précedemment)
|
||||
| +-------- 0 = longueur d'horloge désactivé
|
||||
| 1 = démarrage linéaire du compteur
|
||||
+--------------------------------------------------------------------------------------------------------+
|
||||
|Registre $4009: Registre de contrôle #2 de la voix de triangle (Ecriture seulement)
|
||||
|
|
||||
| Ce registre n'est pas utilisé.
|
||||
|
|
||||
+--------------------------------------------------------------------------------------------------------+
|
||||
|Registre $400A: Registre de fréquence #1 de la voix de triangle (Ecriture seulement)
|
||||
|
|
||||
| Note: la fréquence de la voix de triangle s'étale sur 11 bits.
|
||||
| Ce registre permet de déterminer la valeur des 8 bits de
|
||||
| poids faible de la fréquence.
|
||||
|
|
||||
| masque:%ffffffff -> 8 bits de poids faible de la fréquence
|
||||
|
|
||||
+--------------------------------------------------------------------------------------------------------+
|
||||
|Registre $400B: Registre de fréquence #2 de la voix de triangle (Ecriture seulement)
|
||||
|
|
||||
| masque: %tttttfff
|
||||
| |||||+--- les 3 bits de poids fort de la fréquence de la voix de triangle
|
||||
| +-------- temps d'activité de la voix
|
||||
+--------------------------------------------------------------------------------------------------------+
|
||||
|Registre $400C: Registre de contrôle #1 du canal de bruit (Ecriture seulement)
|
||||
|
|
||||
| masque:%00vessss
|
||||
| ||||+---- Taux d'echantillonage en lecture
|
||||
| |||+----- Selection d'envellope (0 = variée, 1 = fixée)
|
||||
| ||+------ Vague maintenue: répétion automatique (0 = non, 1 = oui)
|
||||
| +-------- Non utilisés
|
||||
+--------------------------------------------------------------------------------------------------------+
|
||||
|Registre $400D: Registre de contrôle #2 du canal de bruit (Ecriture seulement)
|
||||
|
|
||||
| Ce registre n'est pas utilisé.
|
||||
|
|
||||
+--------------------------------------------------------------------------------------------------------+
|
||||
|Registre $400E: Registre de fréquence #1 du canal de bruit (Ecriture seulement)
|
||||
|
|
||||
| Note: la fréquence du canal de bruit s'étale sur 11 bits.
|
||||
| Ce registre permet de déterminer la valeur des 8 bits de
|
||||
| poids faible de la fréquence.
|
||||
|
|
||||
| masque:%ffffffff -> 8 bits de poids faible de la fréquence
|
||||
|
|
||||
+--------------------------------------------------------------------------------------------------------+
|
||||
|Registre $400F: Registre de fréquence #2 du canal de bruit (Ecriture seulement)
|
||||
|
|
||||
| masque: %tttttfff
|
||||
| |||||+--- les 3 bits de poids fort de la fréquence du canal de bruit
|
||||
| +-------- temps d'activité de la voix
|
||||
+--------------------------------------------------------------------------------------------------------+
|
||||
|Registre $4010: Registre de contrôle du canal de son digital (DMC) (Ecriture seulement)
|
||||
|
|
||||
| masque:%i???ssss
|
||||
| ||||+---- fréquence d'échantillonage utilisée
|
||||
| |+------- fonction inconnue
|
||||
| +-------- génére une interruption de type IRQ à l'execution (peut être masquée)
|
||||
|
|
||||
+--------------------------------------------------------------------------------------------------------+
|
||||
|Registre $4011: Registre du volume du canal de son digital DMC (Ecriture seulement)
|
||||
|
|
||||
| Les 8 bits sont utilisés pour définir le volume du canal.
|
||||
|
|
||||
+--------------------------------------------------------------------------------------------------------+
|
||||
|Registre $4012: Registre d'adresse des données digitales du canal DMC (Ecriture seulement)
|
||||
|
|
||||
| Ces 8 bits détermine l'endroit où on doit lire les données pour
|
||||
| le canal DMC.
|
||||
| Les données seront lues dans la 2ème banque de PRG-ROM, c'est à dire de $C000 à $FFFF.
|
||||
| La formule pour trouver l'adresse est la suivante:
|
||||
| ((valeur 8 bits stockée dans $4012) * 64) + $C000
|
||||
| Rappel: 64 = $40
|
||||
|
|
||||
+--------------------------------------------------------------------------------------------------------+
|
||||
|Registre $4013: Registre de longueur des données digitales (Ecriture seulement)
|
||||
|
|
||||
| Définit la longueur de l'échantillon digital à lire à l'adresse $4012
|
||||
| La formule pour calculer sa taille est:
|
||||
| ((valeur 8 bits stockée dans $4013) * 16) + 1
|
||||
|
|
||||
+--------------------------------------------------------------------------------------------------------+
|
||||
|Registre $4014: Registre d'accès direct à la mémoire (DMA) SPR-RAM (Ecriture seulement)
|
||||
|
|
||||
| Voir PPU - Registre spécial.
|
||||
|
|
||||
+--------------------------------------------------------------------------------------------------------+
|
||||
|Registre $4015: Registre d'état de la PPU (Lecture-Ecriture)
|
||||
|
|
||||
| En lecture, ce registre à seulement une fonction:
|
||||
| Seul le bit #0 est important, si il est à 1 c'est que la PPU est en cours d'utilisation
|
||||
|
|
||||
| En écriture:
|
||||
| masque:%000edcba
|
||||
| |||||||+- voix de rythme #1 (0 = désactivée, 1 = activée)
|
||||
| ||||||+-- voix de rythme #2 (0 = désactivée, 1 = activée)
|
||||
| |||||+--- voix de triangle (0 = désactivée, 1 = activée)
|
||||
| ||||+---- canal de bruit (0 = désactivée, 1 = activée)
|
||||
| |||+----- canal digital (0 = désactivée, 1 = activée)
|
||||
| +-------- bits non utilisés
|
||||
|
|
||||
|
|
||||
+--------------------------------------------------------------------------------------------------------+
|
||||
|
||||
Les registres de contrôle des canaux sonores servent juste à configurer les canaux (généralement ceci est fait
|
||||
une seule fois avant de jouer la chanson). Les "notes" sont envoyées par le biais des registres de fréquences
|
||||
avec la fréquence utilisée ainsi que la durée.
|
||||
Le mieux que je puisse vous conseiller c'est de tester ces canaux et d'essayer d'implémenter des partitions
|
||||
récupérées dans des fichiers NSF. Il existe de nombreuses documentations qui expliquent comment procéder. Il
|
||||
existe également un logiciel sur le site NES Development permettant facilement d'écrire une musique et de
|
||||
l'implémenter ensuite dans une rom.
|
||||
|
||||
|
||||
3. Registres Joypads
|
||||
|
||||
|
||||
+--------------------------------------------------------------------------------------------------------+
|
||||
|Registre $4016: Registre de contrôle du Joypad #1 (Lecture-Ecriture)
|
||||
|
|
||||
| En lecture:
|
||||
| %???zteed
|
||||
| |||||||+----- donnée du joypad (voir détails périphériques)
|
||||
| |||||+------- lire des données du port d'expansion (utilisé par certains périphériques)
|
||||
| ||||+-------- indique la gachette du Zapper a été préssée
|
||||
| |||+--------- indique qu'un sprite se trouve devant le viseur du Zapper
|
||||
| +------------ utilisation inconnue
|
||||
|
|
||||
| En écriture:
|
||||
| %?????ees
|
||||
| |||||||+----- Mise à jour état des touches du joypad (nécessaire pour lecture des touches)
|
||||
| ||||||| 0 = efface l'état
|
||||
| ||||||| 1 = reset l'état
|
||||
| ||||||| Voir détails précedemment.
|
||||
| |||||+------- écriture de données dans le port d'expansion
|
||||
| +------------ utisation inconnue
|
||||
+--------------------------------------------------------------------------------------------------------+
|
||||
|Registre $4017: Registre de contrôle du Joypad #2 (Lecture-Ecriture)
|
||||
|
|
||||
| Son fonctionnement est identique à celui du registre précedent.
|
||||
|
|
||||
+--------------------------------------------------------------------------------------------------------+
|
||||
|
||||
|
||||
Autres choses utiles:
|
||||
--------------------
|
||||
|
||||
1. Structure iNes:
|
||||
|
||||
Ici ne sera abordé que la structure iNes, qui utilise un header de 16 octets des fichiers .NES.
|
||||
Cet header ne fait pas vraiment parti de la rom mais contient des informations indispensables
|
||||
à son fonctionnement pour un emulateur (notamment si on utilise un mapper particulier).
|
||||
C'est un standart de fait et il existe d'autres formats (voir meilleurs).
|
||||
|
||||
L'header iNes est de 16 octets($10):
|
||||
|
||||
Adresse |Taille |Contenu
|
||||
| |
|
||||
0 |3 |Contient les 3 caractères ASCII 'NES'
|
||||
| |
|
||||
3 |1 |Contient la valeur $1A
|
||||
| |
|
||||
4 |1 |Nombre de pages de PRG-ROM de 16 ko dans la rom
|
||||
| |
|
||||
5 |1 |Nombre de pages de CHR-ROM de 8 ko dans la rom
|
||||
| |
|
||||
6 |1 |Octet de controle de la ROM #1:
|
||||
| |masque: %mmmmvtsm
|
||||
| | |||||||+-- Miroir utilisé en VRAM (0=horizontal,1=vertical)
|
||||
| | ||||||+--- utilisation de SRAM ou non pour les sauvegardes (1=oui)
|
||||
| | |||||+---- presence d'un trainer de 512 octets
|
||||
| | ||||+----- Possibilité d'utiliser 4 Name Table indépendantes en VRAM
|
||||
| | +--------- 4 bits de poids faible du mapper utilisé
|
||||
| |
|
||||
7 |1 |Octet de controle de la ROM #2:
|
||||
| |masque: %mmmm0000
|
||||
| | ||||+------ ces 4 bits sont toujours à 0
|
||||
| | +---------- 4 bits de poids faible du mapper utilisé
|
||||
| |
|
||||
| |
|
||||
| |Remarque: les octets suivants sont ignorés par la plupart des émulateurs et doivent rester à 0
|
||||
---------------------------------------------------------------------------------------------------------------
|
||||
|
||||
Normalement il est d'usage de faire suivre le code (PRG-ROM) juste après l'header.
|
||||
Les pages CHR-ROM suivront donc logiquement et seront automatiquement chargées dans
|
||||
les tables des graphismes. L'utilisation de la CHR-ROM annihile toute possibilité
|
||||
d'écrire dans la VRAM de $0000 à $2000 sous peine de provoquer une erreur de bus,
|
||||
cette mémoire étant dorénavant considérée comme en disponible en lecture seule.
|
||||
On peut ne mettre le code qu'en utilisant que la deuxième page également.
|
||||
Si vous utilisez +de 2 page de PRG-ROM et +de 2 pages de CHR-ROM il vous
|
||||
faudra utiliser un mapper.
|
||||
Si vous n'utilisez qu'une page de PRG-ROM il s'agit toujours de la page commençant en $C000.
|
||||
|
||||
2. Multi-Memory Controler:
|
||||
|
||||
L'utilisation du Multi-Memory Controller est indispensable pour obtenir une taille de rom >40 ko.
|
||||
Son utilisation va de paire avec ce que l'on appele un mapper. Le mapper utilisé est défini dans l'header.
|
||||
Son support par les émulateurs fut longtemps un problème. Il n'y aura pas beaucoup de détails sur cette
|
||||
partie car la mémoire disponible en standard par une rom est largement suffisante pour une démo.
|
||||
|
||||
Tous les mappers sont détaillés à cette adresse:
|
||||
|
||||
http://free.prohosting.com/~nintendo/mappers.nfo
|
||||
|
||||
|
||||
Détails sur les méthodes d'assemblage:
|
||||
-------------------------------------
|
||||
|
||||
La NES est une machine très ancienne et relativement limitée, néanmoins en assembleur il est possible de tirer d'une
|
||||
machine de nombreuses ressources puisque l'on travaille directement avec le matériel.
|
||||
Mais il convient alors t'integrer des méthodes de programmation particulières, ainsi que d'autres propres
|
||||
au système proprement dit.
|
||||
|
||||
Il ne faut surtout pas hésiter à ménager la mémoire disponible en utilisant abondamment le masquage de bit pour les
|
||||
valeurs ne nécéssitant pas beaucoup de combinaisons. C'est à dire utiliser un octet pour définir sur des plages de
|
||||
bits (flags) des valeurs.
|
||||
|
||||
On peut procéder ainsi:
|
||||
|
||||
Si le monstre reponds masque suivant:
|
||||
____________
|
||||
| | | |
|
||||
bbbb bbb b
|
||||
| | +---- bit de vérification d'activation du monstre
|
||||
| +------- flag de 3 bits pour la vie du monstre
|
||||
+----------- flag de 4 bits pour le type de monstre
|
||||
|
||||
;on veut coder un monstre
|
||||
;le nombre maximum de point de vie possible pour un monstre dans mon jeu est 3
|
||||
;il y a 15 monstres différents et on doit aussi avoir une valeur pour voir si il est actif ou non
|
||||
LDA monstre_1
|
||||
AND #$F0
|
||||
LSR A
|
||||
LSR A
|
||||
LSR A
|
||||
LSR A
|
||||
PHA
|
||||
JSR type_monstre ;routine pour identifier le monstre
|
||||
LDA monstre_1
|
||||
AND #$0E
|
||||
LSR A
|
||||
PHA
|
||||
JSR degats_monstre
|
||||
|
||||
|
||||
|
||||
Sur NES notamment, on ne dispose pas d'horloge intégré comme sur un micro-ordinateur (du moins à partir de 1990 la plupart
|
||||
en ont été équipé). Par contre pour palier à ce défaut, on peut utiliser le signal VBlank cablé en hardware répondant
|
||||
à partir d'un registre particulier et d'un routine d'interruption.
|
||||
Deux moyens répondant toutes deux à des besoins différents. Il faut savoir que sur la NES réelle écrire en mémoire vidéo
|
||||
hors que lors du déroulement du VBlank est une source de bug innérante. Par conséquent on est obligé de s'imposer une
|
||||
certaine rigueur de programmation. Rigueur qui n'est pas obligatoirement reproductible lorsque l'on n'a comme seul moyen
|
||||
de test un émulateur.
|
||||
|
||||
Il existe 2 moyens pour utiliser le VBlank:
|
||||
-soit activer le bit #7 du registre $2000 de la PPU pour generer automatique une interruption non masquable et donc
|
||||
appeler la sous routine d'adresse définie par le vecteur situé en $FFFA. Ceci fait vous pouvez placer absolument tout
|
||||
le code que vous voulez du moment que ca n'excede pas la durée du VBlank en éxecution qui est d'environ 16 millisecondes.
|
||||
Ca suffit normalement pour placer une quantité de code relativement suffisante.
|
||||
Il faut bien réaliser que cette routine sera éxécuté 60x par seconde (pour une machine NTSC, par défaut sur la plupart
|
||||
des émulateurs).
|
||||
|
||||
-l'autre moyen c'est de controler lorsqu'un VBlank survient par le biais du bit #7 du registre $2002. Il suffit de créer
|
||||
une routine qui boucle indéfinimment tant que cette condition n'est pas satisfaite. Normalement cela ne pose pas de problème.
|
||||
Attention si vous utilisez également les NMI ainsi que cette méthode car la NMI sera toujours satisfaite d'abord et
|
||||
ensuite uniquement votre routine sera éxecuté.
|
||||
En outre après la lecture du registre $2002 le bit #7 sera automatiquement remis à 0. Un détail qui a une importance
|
||||
majeure puisqu'il va éviter de réexecuter une seconde fois la routine, ce qui fausserait complétement le programme.
|
||||
|
||||
|
||||
|
||||
Epilogue:
|
||||
--------
|
||||
|
||||
Je tiens à remercier tous les auteurs des documentations déjà disponibles en anglais, notamment Y0shi, Chris Covell
|
||||
dont la plupart de ce document à été constitué, ainsi que la plupart des auteurs de documentations disponibles
|
||||
sur l'excellent site NES Development: http://nesdev.parodius.com.
|
||||
Sans eux rien n'aurait été possible.
|
||||
Je tiens également à remercier ceux qui vont utiliser et distribuer ce document, et j'encourage vivement ce type
|
||||
d'initiatives. J'éspère sincérement qu'il vous sera utile autant qu'il m'a été agréable de l'écrire.
|
||||
|
||||
Détails et remarques: crispysix@wanadoo.fr
|
||||
|
||||
------------------------- c'est ------------------ terminé ---------------------------
|
||||
|
||||
BIN
osx/English.lproj/InfoPlist.strings
Executable file
BIN
osx/English.lproj/InfoPlist.strings
Executable file
Binary file not shown.
19
osx/English.lproj/SDLMain.nib/classes.nib
generated
Normal file
19
osx/English.lproj/SDLMain.nib/classes.nib
generated
Normal file
@@ -0,0 +1,19 @@
|
||||
{
|
||||
IBClasses = (
|
||||
{CLASS = FirstResponder; LANGUAGE = ObjC; SUPERCLASS = NSObject; },
|
||||
{
|
||||
ACTIONS = {
|
||||
help = id;
|
||||
newGame = id;
|
||||
openGame = id;
|
||||
prefsMenu = id;
|
||||
saveGame = id;
|
||||
saveGameAs = id;
|
||||
};
|
||||
CLASS = SDLMain;
|
||||
LANGUAGE = ObjC;
|
||||
SUPERCLASS = NSObject;
|
||||
}
|
||||
);
|
||||
IBVersion = 1;
|
||||
}
|
||||
21
osx/English.lproj/SDLMain.nib/info.nib
generated
Normal file
21
osx/English.lproj/SDLMain.nib/info.nib
generated
Normal file
@@ -0,0 +1,21 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>IBDocumentLocation</key>
|
||||
<string>62 117 356 240 0 0 1152 848 </string>
|
||||
<key>IBEditorPositions</key>
|
||||
<dict>
|
||||
<key>29</key>
|
||||
<string>62 362 195 44 0 0 1152 848 </string>
|
||||
</dict>
|
||||
<key>IBFramework Version</key>
|
||||
<string>291.0</string>
|
||||
<key>IBOpenObjects</key>
|
||||
<array>
|
||||
<integer>29</integer>
|
||||
</array>
|
||||
<key>IBSystem Version</key>
|
||||
<string>6L60</string>
|
||||
</dict>
|
||||
</plist>
|
||||
BIN
osx/English.lproj/SDLMain.nib/objects.nib
generated
Normal file
BIN
osx/English.lproj/SDLMain.nib/objects.nib
generated
Normal file
Binary file not shown.
28
osx/Info.plist
Normal file
28
osx/Info.plist
Normal file
@@ -0,0 +1,28 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>English</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>${EXECUTABLE_NAME}</string>
|
||||
<key>CFBundleIconFile</key>
|
||||
<string></string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>com.yourcompany.nes</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>${PRODUCT_NAME}</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1.0</string>
|
||||
<key>NSMainNibFile</key>
|
||||
<string>SDLMain</string>
|
||||
<key>NSPrincipalClass</key>
|
||||
<string>NSApplication</string>
|
||||
</dict>
|
||||
</plist>
|
||||
17
osx/SDLMain.h
Normal file
17
osx/SDLMain.h
Normal file
@@ -0,0 +1,17 @@
|
||||
/* SDLMain.m - main entry point for our Cocoa-ized SDL app
|
||||
Initial Version: Darrell Walisser <dwaliss1@purdue.edu>
|
||||
Non-NIB-Code & other changes: Max Horn <max@quendi.de>
|
||||
|
||||
Feel free to customize this file to suit your needs
|
||||
*/
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
@interface SDLMain : NSObject
|
||||
- (IBAction)prefsMenu:(id)sender;
|
||||
- (IBAction)newGame:(id)sender;
|
||||
- (IBAction)openGame:(id)sender;
|
||||
- (IBAction)saveGame:(id)sender;
|
||||
- (IBAction)saveGameAs:(id)sender;
|
||||
- (IBAction)help:(id)sender;
|
||||
@end
|
||||
436
osx/SDLMain.m
Normal file
436
osx/SDLMain.m
Normal file
@@ -0,0 +1,436 @@
|
||||
/* SDLMain.m - main entry point for our Cocoa-ized SDL app
|
||||
Initial Version: Darrell Walisser <dwaliss1@purdue.edu>
|
||||
Non-NIB-Code & other changes: Max Horn <max@quendi.de>
|
||||
|
||||
Feel free to customize this file to suit your needs
|
||||
*/
|
||||
|
||||
#import "SDL.h"
|
||||
#import "SDLMain.h"
|
||||
#import <sys/param.h> /* for MAXPATHLEN */
|
||||
#import <unistd.h>
|
||||
|
||||
/* For some reaon, Apple removed setAppleMenu from the headers in 10.4,
|
||||
but the method still is there and works. To avoid warnings, we declare
|
||||
it ourselves here. */
|
||||
@interface NSApplication(SDL_Missing_Methods)
|
||||
- (void)setAppleMenu:(NSMenu *)menu;
|
||||
@end
|
||||
|
||||
/* Use this flag to determine whether we use SDLMain.nib or not */
|
||||
#define SDL_USE_NIB_FILE 1
|
||||
|
||||
/* Use this flag to determine whether we use CPS (docking) or not */
|
||||
#define SDL_USE_CPS 1
|
||||
#ifdef SDL_USE_CPS
|
||||
/* Portions of CPS.h */
|
||||
typedef struct CPSProcessSerNum
|
||||
{
|
||||
UInt32 lo;
|
||||
UInt32 hi;
|
||||
} CPSProcessSerNum;
|
||||
|
||||
extern OSErr CPSGetCurrentProcess( CPSProcessSerNum *psn);
|
||||
extern OSErr CPSEnableForegroundOperation( CPSProcessSerNum *psn, UInt32 _arg2, UInt32 _arg3, UInt32 _arg4, UInt32 _arg5);
|
||||
extern OSErr CPSSetFrontProcess( CPSProcessSerNum *psn);
|
||||
|
||||
#endif /* SDL_USE_CPS */
|
||||
|
||||
static int gArgc;
|
||||
static char **gArgv;
|
||||
static BOOL gFinderLaunch;
|
||||
static BOOL gCalledAppMainline = FALSE;
|
||||
|
||||
static NSString *getApplicationName(void)
|
||||
{
|
||||
NSDictionary *dict;
|
||||
NSString *appName = 0;
|
||||
|
||||
/* Determine the application name */
|
||||
dict = (NSDictionary *)CFBundleGetInfoDictionary(CFBundleGetMainBundle());
|
||||
if (dict)
|
||||
appName = [dict objectForKey: @"CFBundleName"];
|
||||
|
||||
if (![appName length])
|
||||
appName = [[NSProcessInfo processInfo] processName];
|
||||
|
||||
return appName;
|
||||
}
|
||||
|
||||
#if SDL_USE_NIB_FILE
|
||||
/* A helper category for NSString */
|
||||
@interface NSString (ReplaceSubString)
|
||||
- (NSString *)stringByReplacingRange:(NSRange)aRange with:(NSString *)aString;
|
||||
@end
|
||||
#endif
|
||||
|
||||
@interface SDLApplication : NSApplication
|
||||
@end
|
||||
|
||||
@implementation SDLApplication
|
||||
/* Invoked from the Quit menu item */
|
||||
- (void)terminate:(id)sender
|
||||
{
|
||||
/* Post a SDL_QUIT event */
|
||||
SDL_Event event;
|
||||
event.type = SDL_QUIT;
|
||||
SDL_PushEvent(&event);
|
||||
}
|
||||
@end
|
||||
|
||||
/* The main class of the application, the application's delegate */
|
||||
@implementation SDLMain
|
||||
|
||||
- (IBAction)prefsMenu:(id)sender
|
||||
{
|
||||
printf ("prefs menu\n");
|
||||
}
|
||||
|
||||
- (IBAction)newGame:(id)sender
|
||||
{
|
||||
printf ("new game\n");
|
||||
|
||||
NSRunAlertPanel (@"Get ready to blow up some... stuff!",
|
||||
@"Click OK to begin total carnage. Click Cancel to prevent total carnage.", @"OK", @"Cancel", nil);
|
||||
}
|
||||
|
||||
- (IBAction)openGame:(id)sender
|
||||
{
|
||||
NSString *path = nil;
|
||||
NSOpenPanel *openPanel = [ NSOpenPanel openPanel ];
|
||||
|
||||
if ( [ openPanel runModalForDirectory:nil
|
||||
file:@"SavedGame" types:nil ] ) {
|
||||
|
||||
path = [ [ openPanel filenames ] objectAtIndex:0 ];
|
||||
}
|
||||
|
||||
printf ("open game: %s\n", [ path cString ]);
|
||||
}
|
||||
|
||||
- (IBAction)saveGame:(id)sender
|
||||
{
|
||||
NSString *path = nil;
|
||||
NSSavePanel *savePanel = [ NSSavePanel savePanel ];
|
||||
|
||||
if ( [ savePanel runModalForDirectory:nil
|
||||
file:@"SaveGameFile" ] ) {
|
||||
|
||||
path = [ savePanel filename ];
|
||||
}
|
||||
|
||||
printf ("save game: %s\n", [ path cString ]);
|
||||
}
|
||||
|
||||
- (IBAction)saveGameAs:(id)sender
|
||||
{
|
||||
printf ("save game as\n");
|
||||
}
|
||||
|
||||
- (IBAction)help:(id)sender
|
||||
{
|
||||
NSRunAlertPanel (@"Oh help, where have ye gone?",
|
||||
@"Sorry, there is no help available.\n\nThis message brought to you by We Don't Document, Inc.\n\n", @"Rats", @"Good, I never read it anyway", nil);
|
||||
}
|
||||
|
||||
|
||||
/* Set the working directory to the .app's parent directory */
|
||||
- (void) setupWorkingDirectory:(BOOL)shouldChdir
|
||||
{
|
||||
if (shouldChdir)
|
||||
{
|
||||
char parentdir[MAXPATHLEN];
|
||||
CFURLRef url = CFBundleCopyBundleURL(CFBundleGetMainBundle());
|
||||
CFURLRef url2 = CFURLCreateCopyDeletingLastPathComponent(0, url);
|
||||
if (CFURLGetFileSystemRepresentation(url2, true, (UInt8 *)parentdir, MAXPATHLEN)) {
|
||||
assert ( chdir (parentdir) == 0 ); /* chdir to the binary app's parent */
|
||||
}
|
||||
CFRelease(url);
|
||||
CFRelease(url2);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#if SDL_USE_NIB_FILE
|
||||
|
||||
/* Fix menu to contain the real app name instead of "SDL App" */
|
||||
- (void)fixMenu:(NSMenu *)aMenu withAppName:(NSString *)appName
|
||||
{
|
||||
NSRange aRange;
|
||||
NSEnumerator *enumerator;
|
||||
NSMenuItem *menuItem;
|
||||
|
||||
aRange = [[aMenu title] rangeOfString:@"SDL App"];
|
||||
if (aRange.length != 0)
|
||||
[aMenu setTitle: [[aMenu title] stringByReplacingRange:aRange with:appName]];
|
||||
|
||||
enumerator = [[aMenu itemArray] objectEnumerator];
|
||||
while ((menuItem = [enumerator nextObject]))
|
||||
{
|
||||
aRange = [[menuItem title] rangeOfString:@"SDL App"];
|
||||
if (aRange.length != 0)
|
||||
[menuItem setTitle: [[menuItem title] stringByReplacingRange:aRange with:appName]];
|
||||
if ([menuItem hasSubmenu])
|
||||
[self fixMenu:[menuItem submenu] withAppName:appName];
|
||||
}
|
||||
[ aMenu sizeToFit ];
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
static void setApplicationMenu(void)
|
||||
{
|
||||
/* warning: this code is very odd */
|
||||
NSMenu *appleMenu;
|
||||
NSMenuItem *menuItem;
|
||||
NSString *title;
|
||||
NSString *appName;
|
||||
|
||||
appName = getApplicationName();
|
||||
appleMenu = [[NSMenu alloc] initWithTitle:@""];
|
||||
|
||||
/* Add menu items */
|
||||
title = [@"About " stringByAppendingString:appName];
|
||||
[appleMenu addItemWithTitle:title action:@selector(orderFrontStandardAboutPanel:) keyEquivalent:@""];
|
||||
|
||||
[appleMenu addItem:[NSMenuItem separatorItem]];
|
||||
|
||||
title = [@"Hide " stringByAppendingString:appName];
|
||||
[appleMenu addItemWithTitle:title action:@selector(hide:) keyEquivalent:@"h"];
|
||||
|
||||
menuItem = (NSMenuItem *)[appleMenu addItemWithTitle:@"Hide Others" action:@selector(hideOtherApplications:) keyEquivalent:@"h"];
|
||||
[menuItem setKeyEquivalentModifierMask:(NSAlternateKeyMask|NSCommandKeyMask)];
|
||||
|
||||
[appleMenu addItemWithTitle:@"Show All" action:@selector(unhideAllApplications:) keyEquivalent:@""];
|
||||
|
||||
[appleMenu addItem:[NSMenuItem separatorItem]];
|
||||
|
||||
title = [@"Quit " stringByAppendingString:appName];
|
||||
[appleMenu addItemWithTitle:title action:@selector(terminate:) keyEquivalent:@"q"];
|
||||
|
||||
|
||||
/* Put menu into the menubar */
|
||||
menuItem = [[NSMenuItem alloc] initWithTitle:@"" action:nil keyEquivalent:@""];
|
||||
[menuItem setSubmenu:appleMenu];
|
||||
[[NSApp mainMenu] addItem:menuItem];
|
||||
|
||||
/* Tell the application object that this is now the application menu */
|
||||
[NSApp setAppleMenu:appleMenu];
|
||||
|
||||
/* Finally give up our references to the objects */
|
||||
[appleMenu release];
|
||||
[menuItem release];
|
||||
}
|
||||
|
||||
/* Create a window menu */
|
||||
static void setupWindowMenu(void)
|
||||
{
|
||||
NSMenu *windowMenu;
|
||||
NSMenuItem *windowMenuItem;
|
||||
NSMenuItem *menuItem;
|
||||
|
||||
windowMenu = [[NSMenu alloc] initWithTitle:@"Window"];
|
||||
|
||||
/* "Minimize" item */
|
||||
menuItem = [[NSMenuItem alloc] initWithTitle:@"Minimize" action:@selector(performMiniaturize:) keyEquivalent:@"m"];
|
||||
[windowMenu addItem:menuItem];
|
||||
[menuItem release];
|
||||
|
||||
/* Put menu into the menubar */
|
||||
windowMenuItem = [[NSMenuItem alloc] initWithTitle:@"Window" action:nil keyEquivalent:@""];
|
||||
[windowMenuItem setSubmenu:windowMenu];
|
||||
[[NSApp mainMenu] addItem:windowMenuItem];
|
||||
|
||||
/* Tell the application object that this is now the window menu */
|
||||
[NSApp setWindowsMenu:windowMenu];
|
||||
|
||||
/* Finally give up our references to the objects */
|
||||
[windowMenu release];
|
||||
[windowMenuItem release];
|
||||
}
|
||||
|
||||
/* Replacement for NSApplicationMain */
|
||||
static void CustomApplicationMain (int argc, char **argv)
|
||||
{
|
||||
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
|
||||
SDLMain *sdlMain;
|
||||
|
||||
/* Ensure the application object is initialised */
|
||||
[SDLApplication sharedApplication];
|
||||
|
||||
#ifdef SDL_USE_CPS
|
||||
{
|
||||
CPSProcessSerNum PSN;
|
||||
/* Tell the dock about us */
|
||||
if (!CPSGetCurrentProcess(&PSN))
|
||||
if (!CPSEnableForegroundOperation(&PSN,0x03,0x3C,0x2C,0x1103))
|
||||
if (!CPSSetFrontProcess(&PSN))
|
||||
[SDLApplication sharedApplication];
|
||||
}
|
||||
#endif /* SDL_USE_CPS */
|
||||
|
||||
/* Set up the menubar */
|
||||
[NSApp setMainMenu:[[NSMenu alloc] init]];
|
||||
setApplicationMenu();
|
||||
setupWindowMenu();
|
||||
|
||||
/* Create SDLMain and make it the app delegate */
|
||||
sdlMain = [[SDLMain alloc] init];
|
||||
[NSApp setDelegate:sdlMain];
|
||||
|
||||
/* Start the main event loop */
|
||||
[NSApp run];
|
||||
|
||||
[sdlMain release];
|
||||
[pool release];
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
* Catch document open requests...this lets us notice files when the app
|
||||
* was launched by double-clicking a document, or when a document was
|
||||
* dragged/dropped on the app's icon. You need to have a
|
||||
* CFBundleDocumentsType section in your Info.plist to get this message,
|
||||
* apparently.
|
||||
*
|
||||
* Files are added to gArgv, so to the app, they'll look like command line
|
||||
* arguments. Previously, apps launched from the finder had nothing but
|
||||
* an argv[0].
|
||||
*
|
||||
* This message may be received multiple times to open several docs on launch.
|
||||
*
|
||||
* This message is ignored once the app's mainline has been called.
|
||||
*/
|
||||
- (BOOL)application:(NSApplication *)theApplication openFile:(NSString *)filename
|
||||
{
|
||||
const char *temparg;
|
||||
size_t arglen;
|
||||
char *arg;
|
||||
char **newargv;
|
||||
|
||||
if (!gFinderLaunch) /* MacOS is passing command line args. */
|
||||
return FALSE;
|
||||
|
||||
if (gCalledAppMainline) /* app has started, ignore this document. */
|
||||
return FALSE;
|
||||
|
||||
temparg = [filename UTF8String];
|
||||
arglen = SDL_strlen(temparg) + 1;
|
||||
arg = (char *) SDL_malloc(arglen);
|
||||
if (arg == NULL)
|
||||
return FALSE;
|
||||
|
||||
newargv = (char **) realloc(gArgv, sizeof (char *) * (gArgc + 2));
|
||||
if (newargv == NULL)
|
||||
{
|
||||
SDL_free(arg);
|
||||
return FALSE;
|
||||
}
|
||||
gArgv = newargv;
|
||||
|
||||
SDL_strlcpy(arg, temparg, arglen);
|
||||
gArgv[gArgc++] = arg;
|
||||
gArgv[gArgc] = NULL;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
/* Called when the internal event loop has just started running */
|
||||
- (void) applicationDidFinishLaunching: (NSNotification *) note
|
||||
{
|
||||
int status;
|
||||
|
||||
/* Set the working directory to the .app's parent directory */
|
||||
[self setupWorkingDirectory:gFinderLaunch];
|
||||
|
||||
#if SDL_USE_NIB_FILE
|
||||
/* Set the main menu to contain the real app name instead of "SDL App" */
|
||||
[self fixMenu:[NSApp mainMenu] withAppName:getApplicationName()];
|
||||
#endif
|
||||
|
||||
/* Hand off to main application code */
|
||||
gCalledAppMainline = TRUE;
|
||||
status = SDL_main (gArgc, gArgv);
|
||||
|
||||
/* We're done, thank you for playing */
|
||||
exit(status);
|
||||
}
|
||||
@end
|
||||
|
||||
|
||||
@implementation NSString (ReplaceSubString)
|
||||
|
||||
- (NSString *)stringByReplacingRange:(NSRange)aRange with:(NSString *)aString
|
||||
{
|
||||
unsigned int bufferSize;
|
||||
unsigned int selfLen = [self length];
|
||||
unsigned int aStringLen = [aString length];
|
||||
unichar *buffer;
|
||||
NSRange localRange;
|
||||
NSString *result;
|
||||
|
||||
bufferSize = selfLen + aStringLen - aRange.length;
|
||||
buffer = NSAllocateMemoryPages(bufferSize*sizeof(unichar));
|
||||
|
||||
/* Get first part into buffer */
|
||||
localRange.location = 0;
|
||||
localRange.length = aRange.location;
|
||||
[self getCharacters:buffer range:localRange];
|
||||
|
||||
/* Get middle part into buffer */
|
||||
localRange.location = 0;
|
||||
localRange.length = aStringLen;
|
||||
[aString getCharacters:(buffer+aRange.location) range:localRange];
|
||||
|
||||
/* Get last part into buffer */
|
||||
localRange.location = aRange.location + aRange.length;
|
||||
localRange.length = selfLen - localRange.location;
|
||||
[self getCharacters:(buffer+aRange.location+aStringLen) range:localRange];
|
||||
|
||||
/* Build output string */
|
||||
result = [NSString stringWithCharacters:buffer length:bufferSize];
|
||||
|
||||
NSDeallocateMemoryPages(buffer, bufferSize);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
|
||||
#ifdef main
|
||||
# undef main
|
||||
#endif
|
||||
|
||||
|
||||
/* Main entry point to executable - should *not* be SDL_main! */
|
||||
int main (int argc, char **argv)
|
||||
{
|
||||
/* Copy the arguments into a global variable */
|
||||
/* This is passed if we are launched by double-clicking */
|
||||
if ( argc >= 2 && strncmp (argv[1], "-psn", 4) == 0 ) {
|
||||
gArgv = (char **) SDL_malloc(sizeof (char *) * 2);
|
||||
gArgv[0] = argv[0];
|
||||
gArgv[1] = NULL;
|
||||
gArgc = 1;
|
||||
gFinderLaunch = YES;
|
||||
} else {
|
||||
int i;
|
||||
gArgc = argc;
|
||||
gArgv = (char **) SDL_malloc(sizeof (char *) * (argc+1));
|
||||
for (i = 0; i <= argc; i++)
|
||||
gArgv[i] = argv[i];
|
||||
gFinderLaunch = NO;
|
||||
}
|
||||
|
||||
#if SDL_USE_NIB_FILE
|
||||
[SDLApplication poseAsClass:[NSApplication class]];
|
||||
NSApplicationMain (argc, argv);
|
||||
#else
|
||||
CustomApplicationMain (argc, argv);
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
1383
osx/nes.xcodeproj/jbnadal.mode1v3
Normal file
1383
osx/nes.xcodeproj/jbnadal.mode1v3
Normal file
File diff suppressed because it is too large
Load Diff
331
osx/nes.xcodeproj/jbnadal.pbxuser
Normal file
331
osx/nes.xcodeproj/jbnadal.pbxuser
Normal file
@@ -0,0 +1,331 @@
|
||||
// !$*UTF8*$!
|
||||
{
|
||||
002F3A2C09D0888800EBEB88 /* SDLMain.m */ = {
|
||||
uiCtxt = {
|
||||
sepNavIntBoundsRect = "{{0, 0}, {622, 6118}}";
|
||||
sepNavSelRange = "{12415, 36}";
|
||||
sepNavVisRange = "{12046, 458}";
|
||||
sepNavWindowFrame = "{{104, 237}, {750, 558}}";
|
||||
};
|
||||
};
|
||||
29B97313FDCFA39411CA2CEA /* Project object */ = {
|
||||
activeArchitecture = ppc;
|
||||
activeBuildConfigurationName = Release;
|
||||
activeExecutable = EF72319F0D6EFCB90032A59D /* nes */;
|
||||
activeTarget = 8D1107260486CEB800E47090 /* nes */;
|
||||
addToTargets = (
|
||||
8D1107260486CEB800E47090 /* nes */,
|
||||
);
|
||||
breakpoints = (
|
||||
EF72323E0D6F3CE00032A59D /* debugger.c:39 */,
|
||||
);
|
||||
codeSenseManager = EF7231AC0D6EFCD80032A59D /* Code sense */;
|
||||
executables = (
|
||||
EF72319F0D6EFCB90032A59D /* nes */,
|
||||
);
|
||||
perUserDictionary = {
|
||||
PBXConfiguration.PBXFileTableDataSource3.PBXExecutablesDataSource = {
|
||||
PBXFileTableDataSourceColumnSortingDirectionKey = "-1";
|
||||
PBXFileTableDataSourceColumnSortingKey = PBXExecutablesDataSource_NameID;
|
||||
PBXFileTableDataSourceColumnWidthsKey = (
|
||||
22,
|
||||
300,
|
||||
130.58349609375,
|
||||
);
|
||||
PBXFileTableDataSourceColumnsKey = (
|
||||
PBXExecutablesDataSource_ActiveFlagID,
|
||||
PBXExecutablesDataSource_NameID,
|
||||
PBXExecutablesDataSource_CommentsID,
|
||||
);
|
||||
};
|
||||
PBXConfiguration.PBXFileTableDataSource3.PBXFileTableDataSource = {
|
||||
PBXFileTableDataSourceColumnSortingDirectionKey = "-1";
|
||||
PBXFileTableDataSourceColumnSortingKey = PBXFileDataSource_Filename_ColumnID;
|
||||
PBXFileTableDataSourceColumnWidthsKey = (
|
||||
20,
|
||||
243,
|
||||
20,
|
||||
48,
|
||||
43,
|
||||
43,
|
||||
20,
|
||||
);
|
||||
PBXFileTableDataSourceColumnsKey = (
|
||||
PBXFileDataSource_FiletypeID,
|
||||
PBXFileDataSource_Filename_ColumnID,
|
||||
PBXFileDataSource_Built_ColumnID,
|
||||
PBXFileDataSource_ObjectSize_ColumnID,
|
||||
PBXFileDataSource_Errors_ColumnID,
|
||||
PBXFileDataSource_Warnings_ColumnID,
|
||||
PBXFileDataSource_Target_ColumnID,
|
||||
);
|
||||
};
|
||||
PBXConfiguration.PBXFileTableDataSource3.PBXSymbolsDataSource = {
|
||||
PBXFileTableDataSourceColumnSortingDirectionKey = "-1";
|
||||
PBXFileTableDataSourceColumnSortingKey = PBXSymbolsDataSource_SymbolNameID;
|
||||
PBXFileTableDataSourceColumnWidthsKey = (
|
||||
16,
|
||||
200,
|
||||
50,
|
||||
183,
|
||||
);
|
||||
PBXFileTableDataSourceColumnsKey = (
|
||||
PBXSymbolsDataSource_SymbolTypeIconID,
|
||||
PBXSymbolsDataSource_SymbolNameID,
|
||||
PBXSymbolsDataSource_SymbolTypeID,
|
||||
PBXSymbolsDataSource_ReferenceNameID,
|
||||
);
|
||||
};
|
||||
PBXConfiguration.PBXFileTableDataSource3.XCSCMDataSource = {
|
||||
PBXFileTableDataSourceColumnSortingDirectionKey = "-1";
|
||||
PBXFileTableDataSourceColumnSortingKey = PBXFileDataSource_Filename_ColumnID;
|
||||
PBXFileTableDataSourceColumnWidthsKey = (
|
||||
20,
|
||||
20,
|
||||
219,
|
||||
20,
|
||||
48,
|
||||
43,
|
||||
43,
|
||||
20,
|
||||
);
|
||||
PBXFileTableDataSourceColumnsKey = (
|
||||
PBXFileDataSource_SCM_ColumnID,
|
||||
PBXFileDataSource_FiletypeID,
|
||||
PBXFileDataSource_Filename_ColumnID,
|
||||
PBXFileDataSource_Built_ColumnID,
|
||||
PBXFileDataSource_ObjectSize_ColumnID,
|
||||
PBXFileDataSource_Errors_ColumnID,
|
||||
PBXFileDataSource_Warnings_ColumnID,
|
||||
PBXFileDataSource_Target_ColumnID,
|
||||
);
|
||||
};
|
||||
PBXPerProjectTemplateStateSaveDate = 228834200;
|
||||
PBXWorkspaceStateSaveDate = 228834200;
|
||||
};
|
||||
sourceControlManager = EF7231AB0D6EFCD80032A59D /* Source Control */;
|
||||
userBuildSettings = {
|
||||
};
|
||||
};
|
||||
8D1107260486CEB800E47090 /* nes */ = {
|
||||
activeExec = 0;
|
||||
executables = (
|
||||
EF72319F0D6EFCB90032A59D /* nes */,
|
||||
);
|
||||
};
|
||||
EF72319F0D6EFCB90032A59D /* nes */ = {
|
||||
isa = PBXExecutable;
|
||||
activeArgIndices = (
|
||||
YES,
|
||||
);
|
||||
argumentStrings = (
|
||||
"-i /Volumes/Perso/Utilisateurs/jbnadal/sources/sdl/nes/data/testsuite/nestest/nestest.nes",
|
||||
);
|
||||
autoAttachOnCrash = 1;
|
||||
breakpointsEnabled = 0;
|
||||
configStateDict = {
|
||||
};
|
||||
customDataFormattersEnabled = 1;
|
||||
debuggerPlugin = GDBDebugging;
|
||||
disassemblyDisplayState = 0;
|
||||
dylibVariantSuffix = "";
|
||||
enableDebugStr = 1;
|
||||
environmentEntries = (
|
||||
);
|
||||
executableSystemSymbolLevel = 0;
|
||||
executableUserSymbolLevel = 0;
|
||||
libgmallocEnabled = 0;
|
||||
name = nes;
|
||||
savedGlobals = {
|
||||
};
|
||||
sourceDirectories = (
|
||||
);
|
||||
};
|
||||
EF7231AB0D6EFCD80032A59D /* Source Control */ = {
|
||||
isa = PBXSourceControlManager;
|
||||
fallbackIsa = XCSourceControlManager;
|
||||
isSCMEnabled = 0;
|
||||
scmConfiguration = {
|
||||
repositoryName = Nes;
|
||||
};
|
||||
};
|
||||
EF7231AC0D6EFCD80032A59D /* Code sense */ = {
|
||||
isa = PBXCodeSenseManager;
|
||||
indexTemplatePath = "";
|
||||
};
|
||||
EF7231AE0D6EFD080032A59D /* cpu6502.c */ = {
|
||||
uiCtxt = {
|
||||
sepNavIntBoundsRect = "{{0, 0}, {622, 43246}}";
|
||||
sepNavSelRange = "{2251, 0}";
|
||||
sepNavVisRange = "{1718, 462}";
|
||||
sepNavWindowFrame = "{{511, 115}, {750, 558}}";
|
||||
};
|
||||
};
|
||||
EF7231AF0D6EFD080032A59D /* cpu6502.h */ = {
|
||||
uiCtxt = {
|
||||
sepNavIntBoundsRect = "{{0, 0}, {691, 1330}}";
|
||||
sepNavSelRange = "{2214, 0}";
|
||||
sepNavVisRange = "{1592, 631}";
|
||||
sepNavWindowFrame = "{{158, 205}, {750, 558}}";
|
||||
};
|
||||
};
|
||||
EF7231B00D6EFD080032A59D /* cyclesTable.h */ = {
|
||||
uiCtxt = {
|
||||
sepNavIntBoundsRect = "{{0, 0}, {691, 560}}";
|
||||
sepNavSelRange = "{407, 0}";
|
||||
sepNavVisRange = "{0, 1183}";
|
||||
sepNavWindowFrame = "{{511, 115}, {750, 558}}";
|
||||
};
|
||||
};
|
||||
EF7231B10D6EFD080032A59D /* opcodes.h */ = {
|
||||
uiCtxt = {
|
||||
sepNavIntBoundsRect = "{{0, 0}, {691, 2604}}";
|
||||
sepNavSelRange = "{5414, 2}";
|
||||
sepNavVisRange = "{3965, 1638}";
|
||||
sepNavWindowFrame = "{{199, 101}, {750, 558}}";
|
||||
};
|
||||
};
|
||||
EF7231B20D6EFD080032A59D /* debug.c */ = {
|
||||
uiCtxt = {
|
||||
sepNavIntBoundsRect = "{{0, 0}, {622, 378}}";
|
||||
sepNavSelRange = "{442, 0}";
|
||||
sepNavVisRange = "{298, 400}";
|
||||
sepNavWindowFrame = "{{32, 243}, {750, 558}}";
|
||||
};
|
||||
};
|
||||
EF7231B30D6EFD080032A59D /* debug.h */ = {
|
||||
uiCtxt = {
|
||||
sepNavIntBoundsRect = "{{0, 0}, {691, 445}}";
|
||||
sepNavSelRange = "{625, 0}";
|
||||
sepNavVisRange = "{0, 744}";
|
||||
sepNavWindowFrame = "{{153, 142}, {750, 558}}";
|
||||
};
|
||||
};
|
||||
EF7231B50D6EFD080032A59D /* apu.c */ = {
|
||||
uiCtxt = {
|
||||
sepNavIntBoundsRect = "{{0, 0}, {691, 518}}";
|
||||
sepNavSelRange = "{576, 0}";
|
||||
sepNavVisRange = "{243, 517}";
|
||||
sepNavWindowFrame = "{{61, 71}, {750, 558}}";
|
||||
};
|
||||
};
|
||||
EF7231B60D6EFD080032A59D /* apu.h */ = {
|
||||
uiCtxt = {
|
||||
sepNavIntBoundsRect = "{{0, 0}, {691, 430}}";
|
||||
sepNavSelRange = "{285, 0}";
|
||||
sepNavVisRange = "{0, 612}";
|
||||
sepNavWindowFrame = "{{199, 101}, {750, 558}}";
|
||||
};
|
||||
};
|
||||
EF7231B70D6EFD080032A59D /* emulator.c */ = {
|
||||
uiCtxt = {
|
||||
sepNavIntBoundsRect = "{{0, 0}, {691, 6580}}";
|
||||
sepNavSelRange = "{611, 24}";
|
||||
sepNavVisRange = "{125, 692}";
|
||||
sepNavWindowFrame = "{{508, 263}, {750, 558}}";
|
||||
};
|
||||
};
|
||||
EF7231B80D6EFD080032A59D /* emulator.h */ = {
|
||||
uiCtxt = {
|
||||
sepNavIntBoundsRect = "{{0, 0}, {691, 700}}";
|
||||
sepNavSelRange = "{1055, 0}";
|
||||
sepNavVisRange = "{471, 585}";
|
||||
sepNavWindowFrame = "{{222, 80}, {750, 558}}";
|
||||
};
|
||||
};
|
||||
EF7231B90D6EFD080032A59D /* memory.c */ = {
|
||||
uiCtxt = {
|
||||
sepNavIntBoundsRect = "{{0, 0}, {691, 3626}}";
|
||||
sepNavSelRange = "{5624, 0}";
|
||||
sepNavVisRange = "{1925, 752}";
|
||||
sepNavWindowFrame = "{{515, 250}, {750, 558}}";
|
||||
};
|
||||
};
|
||||
EF7231BA0D6EFD080032A59D /* memory.h */ = {
|
||||
uiCtxt = {
|
||||
sepNavIntBoundsRect = "{{0, 0}, {691, 728}}";
|
||||
sepNavSelRange = "{680, 4}";
|
||||
sepNavVisRange = "{437, 961}";
|
||||
sepNavWindowFrame = "{{38, 248}, {750, 558}}";
|
||||
};
|
||||
};
|
||||
EF7231BB0D6EFD080032A59D /* nespal.h */ = {
|
||||
uiCtxt = {
|
||||
sepNavIntBoundsRect = "{{0, 0}, {691, 700}}";
|
||||
sepNavSelRange = "{124, 0}";
|
||||
sepNavVisRange = "{0, 1028}";
|
||||
sepNavWindowFrame = "{{61, 227}, {750, 558}}";
|
||||
};
|
||||
};
|
||||
EF7231BC0D6EFD080032A59D /* ppu.c */ = {
|
||||
uiCtxt = {
|
||||
sepNavIntBoundsRect = "{{0, 0}, {691, 6328}}";
|
||||
sepNavSelRange = "{2933, 0}";
|
||||
sepNavVisRange = "{2517, 956}";
|
||||
sepNavWindowFrame = "{{515, 250}, {750, 558}}";
|
||||
};
|
||||
};
|
||||
EF7231BD0D6EFD080032A59D /* ppu.h */ = {
|
||||
uiCtxt = {
|
||||
sepNavIntBoundsRect = "{{0, 0}, {691, 1764}}";
|
||||
sepNavSelRange = "{2643, 14}";
|
||||
sepNavVisRange = "{0, 1025}";
|
||||
sepNavWindowFrame = "{{160, 102}, {750, 728}}";
|
||||
};
|
||||
};
|
||||
EF7231BE0D6EFD080032A59D /* main.c */ = {
|
||||
uiCtxt = {
|
||||
sepNavIntBoundsRect = "{{0, 0}, {691, 1694}}";
|
||||
sepNavSelRange = "{2382, 0}";
|
||||
sepNavVisRange = "{1902, 681}";
|
||||
sepNavWindowFrame = "{{237, 274}, {750, 558}}";
|
||||
};
|
||||
};
|
||||
EF7231BF0D6EFD080032A59D /* main.h */ = {
|
||||
uiCtxt = {
|
||||
sepNavIntBoundsRect = "{{0, 0}, {691, 938}}";
|
||||
sepNavSelRange = "{1225, 0}";
|
||||
sepNavVisRange = "{872, 1181}";
|
||||
sepNavWindowFrame = "{{84, 206}, {750, 558}}";
|
||||
};
|
||||
};
|
||||
EF7231F50D6F2FAF0032A59D /* debugger.c */ = {
|
||||
uiCtxt = {
|
||||
sepNavIntBoundsRect = "{{0, 0}, {691, 3248}}";
|
||||
sepNavSelRange = "{6274, 21}";
|
||||
sepNavVisRange = "{5699, 720}";
|
||||
sepNavWindowFrame = "{{515, 250}, {750, 558}}";
|
||||
};
|
||||
};
|
||||
EF7232200D6F3A030032A59D /* debugger.h */ = {
|
||||
uiCtxt = {
|
||||
sepNavIntBoundsRect = "{{0, 0}, {691, 445}}";
|
||||
sepNavSelRange = "{671, 0}";
|
||||
sepNavVisRange = "{0, 866}";
|
||||
sepNavWindowFrame = "{{483, 55}, {750, 558}}";
|
||||
};
|
||||
};
|
||||
EF72323E0D6F3CE00032A59D /* debugger.c:39 */ = {
|
||||
isa = PBXFileBreakpoint;
|
||||
actions = (
|
||||
);
|
||||
breakpointStyle = 0;
|
||||
continueAfterActions = 0;
|
||||
countType = 0;
|
||||
delayBeforeContinue = 0;
|
||||
fileReference = EF7231F50D6F2FAF0032A59D /* debugger.c */;
|
||||
functionName = "debuggerMain()";
|
||||
hitCount = 0;
|
||||
ignoreCount = 0;
|
||||
lineNumber = 39;
|
||||
modificationTime = 228757695.663974;
|
||||
state = 2;
|
||||
};
|
||||
EF8A13BC0D92E266004DC967 /* paddle.c */ = {
|
||||
uiCtxt = {
|
||||
sepNavIntBoundsRect = "{{0, 0}, {691, 1120}}";
|
||||
sepNavSelRange = "{939, 0}";
|
||||
sepNavVisRange = "{783, 575}";
|
||||
};
|
||||
};
|
||||
}
|
||||
389
osx/nes.xcodeproj/project.pbxproj
Normal file
389
osx/nes.xcodeproj/project.pbxproj
Normal file
@@ -0,0 +1,389 @@
|
||||
// !$*UTF8*$!
|
||||
{
|
||||
archiveVersion = 1;
|
||||
classes = {
|
||||
};
|
||||
objectVersion = 42;
|
||||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
002F39FA09D0881F00EBEB88 /* SDL.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 002F39F909D0881F00EBEB88 /* SDL.framework */; };
|
||||
002F3A0009D0884600EBEB88 /* SDL.framework in Copy Frameworks into .app bundle */ = {isa = PBXBuildFile; fileRef = 002F39F909D0881F00EBEB88 /* SDL.framework */; };
|
||||
002F3A2E09D0888800EBEB88 /* SDLMain.m in Sources */ = {isa = PBXBuildFile; fileRef = 002F3A2C09D0888800EBEB88 /* SDLMain.m */; };
|
||||
002F3AF109D08F1000EBEB88 /* SDLMain.nib in Resources */ = {isa = PBXBuildFile; fileRef = 002F3AEF09D08F1000EBEB88 /* SDLMain.nib */; };
|
||||
8D11072B0486CEB800E47090 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 089C165CFE840E0CC02AAC07 /* InfoPlist.strings */; };
|
||||
8D11072F0486CEB800E47090 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1058C7A1FEA54F0111CA2CBB /* Cocoa.framework */; };
|
||||
EF7231C00D6EFD080032A59D /* cpu6502.c in Sources */ = {isa = PBXBuildFile; fileRef = EF7231AE0D6EFD080032A59D /* cpu6502.c */; };
|
||||
EF7231C10D6EFD080032A59D /* debug.c in Sources */ = {isa = PBXBuildFile; fileRef = EF7231B20D6EFD080032A59D /* debug.c */; };
|
||||
EF7231C20D6EFD080032A59D /* apu.c in Sources */ = {isa = PBXBuildFile; fileRef = EF7231B50D6EFD080032A59D /* apu.c */; };
|
||||
EF7231C30D6EFD080032A59D /* emulator.c in Sources */ = {isa = PBXBuildFile; fileRef = EF7231B70D6EFD080032A59D /* emulator.c */; };
|
||||
EF7231C40D6EFD080032A59D /* memory.c in Sources */ = {isa = PBXBuildFile; fileRef = EF7231B90D6EFD080032A59D /* memory.c */; };
|
||||
EF7231C50D6EFD080032A59D /* ppu.c in Sources */ = {isa = PBXBuildFile; fileRef = EF7231BC0D6EFD080032A59D /* ppu.c */; };
|
||||
EF7231C60D6EFD080032A59D /* main.c in Sources */ = {isa = PBXBuildFile; fileRef = EF7231BE0D6EFD080032A59D /* main.c */; };
|
||||
EF7231F60D6F2FAF0032A59D /* debugger.c in Sources */ = {isa = PBXBuildFile; fileRef = EF7231F50D6F2FAF0032A59D /* debugger.c */; };
|
||||
EF8A13BF0D92E266004DC967 /* paddle.c in Sources */ = {isa = PBXBuildFile; fileRef = EF8A13BC0D92E266004DC967 /* paddle.c */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXCopyFilesBuildPhase section */
|
||||
002F39FD09D0883400EBEB88 /* Copy Frameworks into .app bundle */ = {
|
||||
isa = PBXCopyFilesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
dstPath = "";
|
||||
dstSubfolderSpec = 10;
|
||||
files = (
|
||||
002F3A0009D0884600EBEB88 /* SDL.framework in Copy Frameworks into .app bundle */,
|
||||
);
|
||||
name = "Copy Frameworks into .app bundle";
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXCopyFilesBuildPhase section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
002F39F909D0881F00EBEB88 /* SDL.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SDL.framework; path = /Library/Frameworks/SDL.framework; sourceTree = "<absolute>"; };
|
||||
002F3A2B09D0888800EBEB88 /* SDLMain.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = SDLMain.h; sourceTree = SOURCE_ROOT; };
|
||||
002F3A2C09D0888800EBEB88 /* SDLMain.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; path = SDLMain.m; sourceTree = SOURCE_ROOT; };
|
||||
002F3AF009D08F1000EBEB88 /* English */ = {isa = PBXFileReference; lastKnownFileType = wrapper.nib; name = English; path = English.lproj/SDLMain.nib; sourceTree = "<group>"; };
|
||||
089C165DFE840E0CC02AAC07 /* English */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = English; path = English.lproj/InfoPlist.strings; sourceTree = "<group>"; };
|
||||
1058C7A1FEA54F0111CA2CBB /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = /System/Library/Frameworks/Cocoa.framework; sourceTree = "<absolute>"; };
|
||||
29B97324FDCFA39411CA2CEA /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = /System/Library/Frameworks/AppKit.framework; sourceTree = "<absolute>"; };
|
||||
29B97325FDCFA39411CA2CEA /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = /System/Library/Frameworks/Foundation.framework; sourceTree = "<absolute>"; };
|
||||
32CA4F630368D1EE00C91783 /* nes_Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = nes_Prefix.pch; sourceTree = "<group>"; };
|
||||
8D1107310486CEB800E47090 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist; path = Info.plist; sourceTree = "<group>"; };
|
||||
8D1107320486CEB800E47090 /* nes.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = nes.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
EF7231AE0D6EFD080032A59D /* cpu6502.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = cpu6502.c; sourceTree = "<group>"; };
|
||||
EF7231AF0D6EFD080032A59D /* cpu6502.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = cpu6502.h; sourceTree = "<group>"; };
|
||||
EF7231B00D6EFD080032A59D /* cyclesTable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = cyclesTable.h; sourceTree = "<group>"; };
|
||||
EF7231B10D6EFD080032A59D /* opcodes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = opcodes.h; sourceTree = "<group>"; };
|
||||
EF7231B20D6EFD080032A59D /* debug.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = debug.c; path = ../src/debug.c; sourceTree = SOURCE_ROOT; };
|
||||
EF7231B30D6EFD080032A59D /* debug.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = debug.h; path = ../src/debug.h; sourceTree = SOURCE_ROOT; };
|
||||
EF7231B50D6EFD080032A59D /* apu.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = apu.c; sourceTree = "<group>"; };
|
||||
EF7231B60D6EFD080032A59D /* apu.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = apu.h; sourceTree = "<group>"; };
|
||||
EF7231B70D6EFD080032A59D /* emulator.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = emulator.c; sourceTree = "<group>"; };
|
||||
EF7231B80D6EFD080032A59D /* emulator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = emulator.h; sourceTree = "<group>"; };
|
||||
EF7231B90D6EFD080032A59D /* memory.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = memory.c; sourceTree = "<group>"; };
|
||||
EF7231BA0D6EFD080032A59D /* memory.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = memory.h; sourceTree = "<group>"; };
|
||||
EF7231BB0D6EFD080032A59D /* nespal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = nespal.h; sourceTree = "<group>"; };
|
||||
EF7231BC0D6EFD080032A59D /* ppu.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ppu.c; sourceTree = "<group>"; };
|
||||
EF7231BD0D6EFD080032A59D /* ppu.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ppu.h; sourceTree = "<group>"; };
|
||||
EF7231BE0D6EFD080032A59D /* main.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = main.c; path = ../src/main.c; sourceTree = SOURCE_ROOT; };
|
||||
EF7231BF0D6EFD080032A59D /* main.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = main.h; path = ../src/main.h; sourceTree = SOURCE_ROOT; };
|
||||
EF7231F50D6F2FAF0032A59D /* debugger.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = debugger.c; sourceTree = "<group>"; };
|
||||
EF7232200D6F3A030032A59D /* debugger.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = debugger.h; sourceTree = "<group>"; };
|
||||
EF8A13BC0D92E266004DC967 /* paddle.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = paddle.c; sourceTree = "<group>"; };
|
||||
EF8A13BD0D92E266004DC967 /* paddle.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = paddle.h; sourceTree = "<group>"; };
|
||||
EF8A13BE0D92E266004DC967 /* ppu_tiles.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ppu_tiles.h; sourceTree = "<group>"; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
8D11072E0486CEB800E47090 /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
002F39FA09D0881F00EBEB88 /* SDL.framework in Frameworks */,
|
||||
8D11072F0486CEB800E47090 /* Cocoa.framework in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXFrameworksBuildPhase section */
|
||||
|
||||
/* Begin PBXGroup section */
|
||||
080E96DDFE201D6D7F000001 /* Classes */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
002F3A2B09D0888800EBEB88 /* SDLMain.h */,
|
||||
002F3A2C09D0888800EBEB88 /* SDLMain.m */,
|
||||
);
|
||||
name = Classes;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
1058C7A0FEA54F0111CA2CBB /* Linked Frameworks */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
002F39F909D0881F00EBEB88 /* SDL.framework */,
|
||||
1058C7A1FEA54F0111CA2CBB /* Cocoa.framework */,
|
||||
);
|
||||
name = "Linked Frameworks";
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
1058C7A2FEA54F0111CA2CBB /* Other Frameworks */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
29B97324FDCFA39411CA2CEA /* AppKit.framework */,
|
||||
29B97325FDCFA39411CA2CEA /* Foundation.framework */,
|
||||
);
|
||||
name = "Other Frameworks";
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
19C28FACFE9D520D11CA2CBB /* Products */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
8D1107320486CEB800E47090 /* nes.app */,
|
||||
);
|
||||
name = Products;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
29B97314FDCFA39411CA2CEA /* nes */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
080E96DDFE201D6D7F000001 /* Classes */,
|
||||
29B97315FDCFA39411CA2CEA /* Other Sources */,
|
||||
29B97317FDCFA39411CA2CEA /* Resources */,
|
||||
29B97323FDCFA39411CA2CEA /* Frameworks */,
|
||||
19C28FACFE9D520D11CA2CBB /* Products */,
|
||||
);
|
||||
name = nes;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
29B97315FDCFA39411CA2CEA /* Other Sources */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
EF7231AD0D6EFD080032A59D /* cpu */,
|
||||
EF7231B20D6EFD080032A59D /* debug.c */,
|
||||
EF7231B30D6EFD080032A59D /* debug.h */,
|
||||
EF7231B40D6EFD080032A59D /* emulator */,
|
||||
EF7231BE0D6EFD080032A59D /* main.c */,
|
||||
EF7231BF0D6EFD080032A59D /* main.h */,
|
||||
32CA4F630368D1EE00C91783 /* nes_Prefix.pch */,
|
||||
);
|
||||
name = "Other Sources";
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
29B97317FDCFA39411CA2CEA /* Resources */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
8D1107310486CEB800E47090 /* Info.plist */,
|
||||
089C165CFE840E0CC02AAC07 /* InfoPlist.strings */,
|
||||
002F3AEF09D08F1000EBEB88 /* SDLMain.nib */,
|
||||
);
|
||||
name = Resources;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
29B97323FDCFA39411CA2CEA /* Frameworks */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
1058C7A0FEA54F0111CA2CBB /* Linked Frameworks */,
|
||||
1058C7A2FEA54F0111CA2CBB /* Other Frameworks */,
|
||||
);
|
||||
name = Frameworks;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
EF7231AD0D6EFD080032A59D /* cpu */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
EF7231AE0D6EFD080032A59D /* cpu6502.c */,
|
||||
EF7231AF0D6EFD080032A59D /* cpu6502.h */,
|
||||
EF7231B00D6EFD080032A59D /* cyclesTable.h */,
|
||||
EF7231B10D6EFD080032A59D /* opcodes.h */,
|
||||
);
|
||||
name = cpu;
|
||||
path = ../src/cpu;
|
||||
sourceTree = SOURCE_ROOT;
|
||||
};
|
||||
EF7231B40D6EFD080032A59D /* emulator */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
EF8A13BC0D92E266004DC967 /* paddle.c */,
|
||||
EF8A13BD0D92E266004DC967 /* paddle.h */,
|
||||
EF8A13BE0D92E266004DC967 /* ppu_tiles.h */,
|
||||
EF7231B50D6EFD080032A59D /* apu.c */,
|
||||
EF7231B60D6EFD080032A59D /* apu.h */,
|
||||
EF7231B70D6EFD080032A59D /* emulator.c */,
|
||||
EF7231B80D6EFD080032A59D /* emulator.h */,
|
||||
EF7231B90D6EFD080032A59D /* memory.c */,
|
||||
EF7231BA0D6EFD080032A59D /* memory.h */,
|
||||
EF7231BB0D6EFD080032A59D /* nespal.h */,
|
||||
EF7231BC0D6EFD080032A59D /* ppu.c */,
|
||||
EF7231BD0D6EFD080032A59D /* ppu.h */,
|
||||
EF7231F50D6F2FAF0032A59D /* debugger.c */,
|
||||
EF7232200D6F3A030032A59D /* debugger.h */,
|
||||
);
|
||||
name = emulator;
|
||||
path = ../src/emulator;
|
||||
sourceTree = SOURCE_ROOT;
|
||||
};
|
||||
/* End PBXGroup section */
|
||||
|
||||
/* Begin PBXNativeTarget section */
|
||||
8D1107260486CEB800E47090 /* nes */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = C01FCF4A08A954540054247B /* Build configuration list for PBXNativeTarget "nes" */;
|
||||
buildPhases = (
|
||||
8D1107290486CEB800E47090 /* Resources */,
|
||||
8D11072C0486CEB800E47090 /* Sources */,
|
||||
8D11072E0486CEB800E47090 /* Frameworks */,
|
||||
002F39FD09D0883400EBEB88 /* Copy Frameworks into .app bundle */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
dependencies = (
|
||||
);
|
||||
name = nes;
|
||||
productInstallPath = "$(HOME)/Applications";
|
||||
productName = nes;
|
||||
productReference = 8D1107320486CEB800E47090 /* nes.app */;
|
||||
productType = "com.apple.product-type.application";
|
||||
};
|
||||
/* End PBXNativeTarget section */
|
||||
|
||||
/* Begin PBXProject section */
|
||||
29B97313FDCFA39411CA2CEA /* Project object */ = {
|
||||
isa = PBXProject;
|
||||
buildConfigurationList = C01FCF4E08A954540054247B /* Build configuration list for PBXProject "nes" */;
|
||||
compatibilityVersion = "Xcode 2.4";
|
||||
hasScannedForEncodings = 1;
|
||||
mainGroup = 29B97314FDCFA39411CA2CEA /* nes */;
|
||||
projectDirPath = "";
|
||||
projectRoot = "";
|
||||
targets = (
|
||||
8D1107260486CEB800E47090 /* nes */,
|
||||
);
|
||||
};
|
||||
/* End PBXProject section */
|
||||
|
||||
/* Begin PBXResourcesBuildPhase section */
|
||||
8D1107290486CEB800E47090 /* Resources */ = {
|
||||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
8D11072B0486CEB800E47090 /* InfoPlist.strings in Resources */,
|
||||
002F3AF109D08F1000EBEB88 /* SDLMain.nib in Resources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXResourcesBuildPhase section */
|
||||
|
||||
/* Begin PBXSourcesBuildPhase section */
|
||||
8D11072C0486CEB800E47090 /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
002F3A2E09D0888800EBEB88 /* SDLMain.m in Sources */,
|
||||
EF7231C00D6EFD080032A59D /* cpu6502.c in Sources */,
|
||||
EF7231C10D6EFD080032A59D /* debug.c in Sources */,
|
||||
EF7231C20D6EFD080032A59D /* apu.c in Sources */,
|
||||
EF7231C30D6EFD080032A59D /* emulator.c in Sources */,
|
||||
EF7231C40D6EFD080032A59D /* memory.c in Sources */,
|
||||
EF7231C50D6EFD080032A59D /* ppu.c in Sources */,
|
||||
EF7231C60D6EFD080032A59D /* main.c in Sources */,
|
||||
EF7231F60D6F2FAF0032A59D /* debugger.c in Sources */,
|
||||
EF8A13BF0D92E266004DC967 /* paddle.c in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXSourcesBuildPhase section */
|
||||
|
||||
/* Begin PBXVariantGroup section */
|
||||
002F3AEF09D08F1000EBEB88 /* SDLMain.nib */ = {
|
||||
isa = PBXVariantGroup;
|
||||
children = (
|
||||
002F3AF009D08F1000EBEB88 /* English */,
|
||||
);
|
||||
name = SDLMain.nib;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
089C165CFE840E0CC02AAC07 /* InfoPlist.strings */ = {
|
||||
isa = PBXVariantGroup;
|
||||
children = (
|
||||
089C165DFE840E0CC02AAC07 /* English */,
|
||||
);
|
||||
name = InfoPlist.strings;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* End PBXVariantGroup section */
|
||||
|
||||
/* Begin XCBuildConfiguration section */
|
||||
C01FCF4B08A954540054247B /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
COPY_PHASE_STRIP = NO;
|
||||
GCC_DYNAMIC_NO_PIC = NO;
|
||||
GCC_ENABLE_FIX_AND_CONTINUE = YES;
|
||||
GCC_MODEL_TUNING = G5;
|
||||
GCC_OPTIMIZATION_LEVEL = 0;
|
||||
INFOPLIST_FILE = Info.plist;
|
||||
INSTALL_PATH = "$(HOME)/Applications";
|
||||
PRODUCT_NAME = nes;
|
||||
WRAPPER_EXTENSION = app;
|
||||
ZERO_LINK = YES;
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
C01FCF4C08A954540054247B /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ARCHS = ppc;
|
||||
GCC_GENERATE_DEBUGGING_SYMBOLS = NO;
|
||||
GCC_MODEL_TUNING = G5;
|
||||
INFOPLIST_FILE = Info.plist;
|
||||
INSTALL_PATH = "$(HOME)/Applications";
|
||||
PRODUCT_NAME = nes;
|
||||
WRAPPER_EXTENSION = app;
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
C01FCF4F08A954540054247B /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
FRAMEWORK_SEARCH_PATHS = (
|
||||
"$(HOME)/Library/Frameworks",
|
||||
/Library/Frameworks,
|
||||
"$(FRAMEWORK_SEARCH_PATHS)",
|
||||
);
|
||||
GCC_C_LANGUAGE_STANDARD = gnu99;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
HEADER_SEARCH_PATHS = (
|
||||
"$(HOME)/Library/Frameworks/SDL.framework/Headers",
|
||||
/Library/Frameworks/SDL.framework/Headers,
|
||||
"$(HEADER_SEARCH_PATHS)",
|
||||
);
|
||||
PREBINDING = NO;
|
||||
SDKROOT = /Developer/SDKs/MacOSX10.4u.sdk;
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
C01FCF5008A954540054247B /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
FRAMEWORK_SEARCH_PATHS = (
|
||||
"$(HOME)/Library/Frameworks",
|
||||
/Library/Frameworks,
|
||||
"$(FRAMEWORK_SEARCH_PATHS)",
|
||||
);
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
HEADER_SEARCH_PATHS = (
|
||||
"$(HOME)/Library/Frameworks/SDL.framework/Headers",
|
||||
/Library/Frameworks/SDL.framework/Headers,
|
||||
"$(HEADER_SEARCH_PATHS)",
|
||||
);
|
||||
PREBINDING = NO;
|
||||
SDKROOT = /Developer/SDKs/MacOSX10.4u.sdk;
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
/* End XCBuildConfiguration section */
|
||||
|
||||
/* Begin XCConfigurationList section */
|
||||
C01FCF4A08A954540054247B /* Build configuration list for PBXNativeTarget "nes" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
C01FCF4B08A954540054247B /* Debug */,
|
||||
C01FCF4C08A954540054247B /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
C01FCF4E08A954540054247B /* Build configuration list for PBXProject "nes" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
C01FCF4F08A954540054247B /* Debug */,
|
||||
C01FCF5008A954540054247B /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
/* End XCConfigurationList section */
|
||||
};
|
||||
rootObject = 29B97313FDCFA39411CA2CEA /* Project object */;
|
||||
}
|
||||
9
osx/nes_Prefix.pch
Normal file
9
osx/nes_Prefix.pch
Normal file
@@ -0,0 +1,9 @@
|
||||
//
|
||||
// Prefix header for all source files of the 'nes' target in the 'nes' project
|
||||
//
|
||||
|
||||
#include "SDL.h"
|
||||
|
||||
#ifdef __OBJC__
|
||||
#import <Cocoa/Cocoa.h>
|
||||
#endif
|
||||
3071
src/cpu/cpu6502.c
Normal file
3071
src/cpu/cpu6502.c
Normal file
File diff suppressed because it is too large
Load Diff
94
src/cpu/cpu6502.h
Normal file
94
src/cpu/cpu6502.h
Normal file
@@ -0,0 +1,94 @@
|
||||
/*----------------------------------------------------------------------------
|
||||
* cpu6502.h:
|
||||
*
|
||||
*----------------------------------------------------------------------------
|
||||
* nes: Nintendo Entertainment System Emulator.
|
||||
*
|
||||
* Created by Jean-Baptiste Nadal on 12/02/08.
|
||||
* Copyright 2008. All rights reserved.
|
||||
*----------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#ifndef __CPU6502_H
|
||||
#define __CPU6502_H
|
||||
|
||||
/*
|
||||
* Status:
|
||||
* ======
|
||||
* +-----+-----+-----+-----+-----+-----+-----+-----+
|
||||
* | | | | | | | | |
|
||||
* | N | V | R | B | D | I | Z | C |
|
||||
* | | | | | | | | |
|
||||
* +-----+-----+-----+-----+-----+-----+-----+-----+
|
||||
*
|
||||
* C: Carry Flag (Bit de retenu)
|
||||
* Z: Zero Flag (Bit d'etat Zero)
|
||||
* I: Interrupt Disable Flag
|
||||
* D: Decimal Mode (Mode décimal)
|
||||
* B: Break Command (Bit de Break)
|
||||
* R: Reserved
|
||||
* V: Overflow Flag (Bit de depassement signé)
|
||||
* N: Negative Flag (Bit de negation)
|
||||
*/
|
||||
|
||||
#define STATUS_FLAG_C 0
|
||||
#define STATUS_FLAG_Z 1
|
||||
#define STATUS_FLAG_I 2
|
||||
#define STATUS_FLAG_D 3
|
||||
#define STATUS_FLAG_B 4
|
||||
#define STATUS_FLAG_R 5
|
||||
#define STATUS_FLAG_V 6
|
||||
#define STATUS_FLAG_N 7
|
||||
|
||||
|
||||
#define FLAG_C 0x1
|
||||
|
||||
#define STACK_START 0xFF
|
||||
#define STACK_END 0x100
|
||||
|
||||
/* Overflow flag */
|
||||
#define V_FLAG 0xFF00
|
||||
#define B_FLAG (1 << STATUS_FLAG_B)
|
||||
// AVANT #define V_FLAG (1 << 6)
|
||||
|
||||
|
||||
/* Structure for the Debug's Alert support of registers. */
|
||||
typedef struct nes_DebugCpuAlert_t {
|
||||
|
||||
/* Boolean to know if an alert is set or not. */
|
||||
uint8_t active;
|
||||
|
||||
int16_t a;
|
||||
int16_t x;
|
||||
int16_t y;
|
||||
|
||||
}nes_DebugCpuAlert_t;
|
||||
|
||||
|
||||
/* Main structure of the NES's CPU. */
|
||||
typedef struct nes_cpu6502_t {
|
||||
uint8_t a;
|
||||
uint8_t x;
|
||||
uint8_t y;
|
||||
uint8_t R_Stack;
|
||||
uint8_t R_Status;
|
||||
uint16_t pc;
|
||||
|
||||
/* Fonction to access to the Central Memory. */
|
||||
uint8_t (*memRead) (uint16_t addr);
|
||||
void (*memWrite) (uint16_t addr, uint8_t value);
|
||||
|
||||
/* Counter of Cycle to know where we are. */
|
||||
int16_t nbCycles;
|
||||
|
||||
/* Alert on the registers. */
|
||||
nes_DebugCpuAlert_t alert;
|
||||
|
||||
}nes_cpu6502_t;
|
||||
|
||||
|
||||
/* Exported functions. */
|
||||
extern int cpuReset (nes_cpu6502_t *cpu);
|
||||
extern int cpu6502_execOpCode (nes_cpu6502_t *cpu, uint8_t opcode);
|
||||
|
||||
#endif
|
||||
40
src/cpu/cyclesTable.h
Normal file
40
src/cpu/cyclesTable.h
Normal file
@@ -0,0 +1,40 @@
|
||||
/*----------------------------------------------------------------------------
|
||||
* cycleTable.h: This file contain the array of the number of cycles, each
|
||||
* opcode take.
|
||||
*
|
||||
*----------------------------------------------------------------------------
|
||||
* nes: Nintendo Entertainment System Emulator.
|
||||
*
|
||||
* Created by Jean-Baptiste Nadal on 12/02/08.
|
||||
* Copyright 2008. All rights reserved.
|
||||
*----------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#ifndef __CYCLETABLE_H
|
||||
#define __CYCLETABLE_H
|
||||
|
||||
|
||||
|
||||
/* Number of cycles for the 256 opcodes of the 6502 CPU. */
|
||||
|
||||
uint8_t OpcodesCycles[256] =
|
||||
{
|
||||
7, 6, 2, 1, 5, 3, 5, 5, 3, 2, 2, 1, 6, 4, 6, 2,
|
||||
2, 5, 5, 1, 5, 4, 6, 5, 2, 4, 2, 1, 6, 4, 6, 2,
|
||||
6, 6, 2, 1, 3, 3, 5, 5, 4, 2, 2, 1, 4, 4, 6, 2,
|
||||
2, 5, 5, 1, 4, 4, 6, 5, 2, 4, 2, 1, 4, 4, 6, 2,
|
||||
6, 6, 2, 1, 3, 3, 5, 5, 3, 2, 2, 1, 3, 4, 6, 2,
|
||||
2, 5, 5, 1, 4, 4, 6, 5, 2, 4, 3, 1, 8, 4, 6, 2,
|
||||
6, 6, 2, 1, 3, 3, 5, 5, 4, 2, 2, 1, 6, 4, 6, 2,
|
||||
2, 5, 5, 1, 4, 4, 6, 5, 5, 4, 4, 1, 6, 4, 6, 2,
|
||||
3, 6, 2, 1, 3, 3, 3, 5, 2, 2, 2, 1, 4, 4, 4, 2,
|
||||
2, 6, 5, 1, 4, 4, 4, 5, 2, 5, 2, 1, 4, 5, 5, 2,
|
||||
2, 6, 2, 1, 3, 3, 3, 5, 2, 2, 2, 1, 4, 4, 4, 2,
|
||||
2, 5, 5, 1, 4, 4, 4, 5, 2, 4, 2, 1, 4, 4, 4, 2,
|
||||
2, 6, 2, 1, 3, 3, 5, 5, 2, 2, 2, 2, 4, 4, 6, 2,
|
||||
2, 5, 5, 1, 4, 4, 6, 5, 2, 4, 3, 2, 4, 4, 6, 2,
|
||||
2, 6, 2, 1, 3, 3, 5, 5, 2, 2, 2, 1, 4, 4, 6, 2,
|
||||
2, 5, 5, 1, 4, 4, 6, 5, 2, 4, 4, 1, 4, 4, 6, 2
|
||||
};
|
||||
|
||||
#endif
|
||||
190
src/cpu/opcodes.h
Normal file
190
src/cpu/opcodes.h
Normal file
@@ -0,0 +1,190 @@
|
||||
/*----------------------------------------------------------------------------
|
||||
* 6502: opcodes.h. This file contain the list of the opcodes of the cpu.
|
||||
*
|
||||
*----------------------------------------------------------------------------
|
||||
* nes: Nintendo Entertainment System Emulator.
|
||||
*
|
||||
* Created by Jean-Baptiste Nadal on 12/02/08.
|
||||
* Copyright 2008. All rights reserved.
|
||||
*----------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
|
||||
#ifndef _6502_OPCODES_H
|
||||
#define _6502_OPCODES_H
|
||||
|
||||
#define OP_CODE_BRK 0x00 /* Break. Soft Interrupt. */
|
||||
|
||||
#define OP_CODE_SEI 0x78 /* SEI - Set Interrupt disable. */
|
||||
#define OP_CODE_SED 0xf8 /* SED - Set Decimal Mode. */
|
||||
#define OP_CODE_SEC 0x38 /* SEC - Set Carry Flag. */
|
||||
#define OP_CODE_CLC 0x18 /* CLC - Clear Carry. */
|
||||
#define OP_CODE_CLD 0xd8 /* Clear Decimal Mode. */
|
||||
#define OP_CODE_CLV 0xb8 /* Clear Overflow Flag. */
|
||||
/* LoaD Accumulator Charge dans le registre A. */
|
||||
#define OP_CODE_LDA_1 0xa9 /* LDA #aa */
|
||||
#define OP_CODE_LDA_2 0xa5 /* LDA $aa */
|
||||
#define OP_CODE_LDA_3 0xb5 /* LDA $aa,X */
|
||||
#define OP_CODE_LDA_4 0xad /* LDA $aaaa */
|
||||
#define OP_CODE_LDA_5 0xbd /* LDA $aaaa, X */
|
||||
#define OP_CODE_LDA_6 0xb9 /* LDA $aaaa, Y */
|
||||
#define OP_CODE_LDA_7 0xa1 /* LDA ($aa,X) */
|
||||
#define OP_CODE_LDA_8 0xb1 /* LDA ($aa),Y */
|
||||
/* Load Register X */
|
||||
#define OP_CODE_LDX_1 0xa2 /* LDX #aa */
|
||||
#define OP_CODE_LDX_2 0xa6 /* LDX $aa */
|
||||
#define OP_CODE_LDX_3 0xb6 /* LDX $aa,Y */
|
||||
#define OP_CODE_LDX_4 0xae /* LDX $aaaa */
|
||||
#define OP_CODE_LDX_5 0xbe /* LDX $aaaa,Y */
|
||||
/* Load Register Y */
|
||||
#define OP_CODE_LDY_1 0xa0 /* LDY #aa */
|
||||
#define OP_CODE_LDY_2 0xa4 /* LDY $aa */
|
||||
#define OP_CODE_LDY_3 0xb4 /* LDY $aa,X */
|
||||
#define OP_CODE_LDY_4 0xac /* LDY $aaaa */
|
||||
#define OP_CODE_LDY_5 0xbc /* LDY $aaaa,X */
|
||||
/* STore Accumulator */
|
||||
#define OP_CODE_STA_1 0x85 /* STA $aa */
|
||||
#define OP_CODE_STA_2 0x95 /* STA $aa,X */
|
||||
#define OP_CODE_STA_3 0x8d /* STA $aaaa */
|
||||
#define OP_CODE_STA_4 0x9d /* STA $aaaa,X */
|
||||
#define OP_CODE_STA_5 0x99 /* STA $aaaa,Y */
|
||||
#define OP_CODE_STA_6 0x81 /* STA ($aa,X) */
|
||||
#define OP_CODE_STA_7 0x91 /* STA ($aa),Y */
|
||||
/* STX - Store X Register */
|
||||
#define OP_CODE_STX_1 0x86 /* STX $aa */
|
||||
#define OP_CODE_STX_2 0x96 /* STX $aa,Y */
|
||||
#define OP_CODE_STX_3 0x8e /* STX $aaaa */
|
||||
/* STY - Store Y Register */
|
||||
#define OP_CODE_STY_1 0x84 /* STY $aa */
|
||||
#define OP_CODE_STY_2 0x94 /* STY $aa,X */
|
||||
#define OP_CODE_STY_3 0x8c /* STY $aaaa */
|
||||
#define OP_CODE_TXS 0x9a /* Transfert X to Stack. */
|
||||
#define OP_CODE_TXA 0x8a /* TXA - Transfer X to Register A */
|
||||
#define OP_CODE_TYA 0x98 /* TYA - Transfer Y to Register A */
|
||||
#define OP_CODE_TAX 0xaa /* TAX - Transfer Accumulator to X */
|
||||
#define OP_CODE_TAY 0xa8 /* TAY - Transfer Accumulator to Y */
|
||||
#define OP_CODE_TSX 0xba /* TSX - Transfer Stack To X */
|
||||
#define OP_CODE_BPL 0x10 /* BPL - Branch on Plus */
|
||||
#define OP_CODE_BMI 0x30 /* BMI - Branch if Minus */
|
||||
/* Compare Memory And Accumulator */
|
||||
#define OP_CODE_CMP_1 0xc9 /* CMP #aa */
|
||||
#define OP_CODE_CMP_2 0xc5 /* CMP $aa */
|
||||
#define OP_CODE_CMP_3 0xd5 /* CMP $aa,X */
|
||||
#define OP_CODE_CMP_4 0xcd /* CMP $aaaa */
|
||||
#define OP_CODE_CMP_5 0xdd /* CMP $aaaa,X */
|
||||
#define OP_CODE_CMP_6 0xd9 /* CMP $aaaa,Y */
|
||||
#define OP_CODE_CMP_7 0xc1 /* CMP ($aa,X) */
|
||||
#define OP_CODE_CMP_8 0xd1 /* CMP ($aa),Y */
|
||||
/* CPX - Compare Memory And X */
|
||||
#define OP_CODE_CPX_1 0xe0 /* CPX #aa */
|
||||
#define OP_CODE_CPX_2 0xe4 /* CPX $aa */
|
||||
#define OP_CODE_CPX_3 0xec /* CPX $aaaa */
|
||||
/* CPY - Compare Memory And Y */
|
||||
#define OP_CODE_CPY_1 0xc0 /* CPY #aa */
|
||||
#define OP_CODE_CPY_2 0xc4 /* CPY $aa */
|
||||
#define OP_CODE_CPY_3 0xcc /* CPY $aaaa */
|
||||
/* ORA - Logical Inclusive OR */
|
||||
#define OP_CODE_ORA_1 0x09 /* ORA #aa */
|
||||
#define OP_CODE_ORA_2 0x05 /* ORA $aa */
|
||||
#define OP_CODE_ORA_3 0x15 /* ORA $aa,X */
|
||||
#define OP_CODE_ORA_4 0x0d /* ORA $aaaa */
|
||||
#define OP_CODE_ORA_5 0x1d /* ORA $aaaa,X */
|
||||
#define OP_CODE_ORA_6 0x19 /* ORA $aaaa,Y */
|
||||
#define OP_CODE_ORA_7 0x01 /* ORA ($aa,X) */
|
||||
#define OP_CODE_ORA_8 0x11 /* ORA ($aa),Y */
|
||||
#define OP_CODE_BCS 0xb0 /* BCS - Branch if Carry Set */
|
||||
#define OP_CODE_BCC 0x90 /* BCC - Branch if Carry Clear */
|
||||
#define OP_CODE_BVC 0x50 /* BVC - Branch if Overflow Clear */
|
||||
#define OP_CODE_BVS 0x70 /* BVS - Branch if Overflow Set */
|
||||
#define OP_CODE_JSR 0x20 /* JSR - Jump to SubRoutine */
|
||||
/* Jump. */
|
||||
#define OP_CODE_JMP_1 0x4c /* JMP $aaaa */
|
||||
#define OP_CODE_JMP_2 0x6c /* JMP ($aaaa) */
|
||||
#define OP_CODE_BNE 0xd0 /* BNE - Branch if not Equal */
|
||||
#define OP_CODE_BEQ 0xf0 /* BEQ - Branch if Equal */
|
||||
#define OP_CODE_DEY 0x88 /* DEY - Decrement Y */
|
||||
#define OP_CODE_DEX 0xca /* DEX - Decrement X */
|
||||
#define OP_CODE_INY 0xc8 /* INY - Increment Y */
|
||||
#define OP_CODE_INX 0xe8 /* INX - Increment X */
|
||||
/* INC - Increment Memory */
|
||||
#define OP_CODE_INC_1 0xe6 /* INC $aa */
|
||||
#define OP_CODE_INC_2 0xf6 /* INC $aa,X */
|
||||
#define OP_CODE_INC_3 0xee /* INC $aaaa */
|
||||
#define OP_CODE_INC_4 0xfe /* INC $aaaa,X */
|
||||
/* DEC - Decrement Source */
|
||||
#define OP_CODE_DEC_1 0xc6 /* DEC $aa */
|
||||
#define OP_CODE_DEC_2 0xd6 /* DEC $aa,X */
|
||||
#define OP_CODE_DEC_3 0xce /* DEC $aaaa */
|
||||
#define OP_CODE_DEC_4 0xde /* DEC $aaaa,X */
|
||||
/* ADC - Add with Carry */
|
||||
#define OP_CODE_ADC_1 0x69 /* ADC #aa */
|
||||
#define OP_CODE_ADC_2 0x65 /* ADC $aa */
|
||||
#define OP_CODE_ADC_3 0x75 /* ADC $aa,X */
|
||||
#define OP_CODE_ADC_4 0x6d /* ADC $aaaa */
|
||||
#define OP_CODE_ADC_5 0x7d /* ADC $aaaa,X */
|
||||
#define OP_CODE_ADC_6 0x79 /* ADC $aaaa,Y */
|
||||
#define OP_CODE_ADC_7 0x61 /* ADC ($aa,X) */
|
||||
#define OP_CODE_ADC_8 0x71 /* ADC ($aa),Y */
|
||||
/* SBC - Substract with Carry */
|
||||
#define OP_CODE_SBC_1 0xe9 /* SBC #aa */
|
||||
#define OP_CODE_SBC_2 0xe5 /* SBC $aa */
|
||||
#define OP_CODE_SBC_3 0xf5 /* SBC $aa,X */
|
||||
#define OP_CODE_SBC_4 0xed /* SBC $aaaa */
|
||||
#define OP_CODE_SBC_5 0xfd /* SBC $aaaa,X */
|
||||
#define OP_CODE_SBC_6 0xf9 /* SBC $aaaa,Y */
|
||||
#define OP_CODE_SBC_7 0xe1 /* SBC ($aa,X) */
|
||||
#define OP_CODE_SBC_8 0xf1 /* SBC ($aa),Y */
|
||||
/* BIT - Bit Test */
|
||||
#define OP_CODE_BIT_1 0x24 /* BIT $aa */
|
||||
#define OP_CODE_BIT_2 0x2c /* BIT $aaaa */
|
||||
/* AND - Logical AND */
|
||||
#define OP_CODE_AND_1 0x29 /* AND #aa */
|
||||
#define OP_CODE_AND_2 0x25 /* AND $aa */
|
||||
#define OP_CODE_AND_3 0x35 /* AND $aa,X */
|
||||
#define OP_CODE_AND_4 0x2d /* AND $aaaa */
|
||||
#define OP_CODE_AND_5 0x3d /* AND $aaaa,X */
|
||||
#define OP_CODE_AND_6 0x39 /* AND $aaaa,Y */
|
||||
#define OP_CODE_AND_7 0x21 /* AND ($aa,X) */
|
||||
#define OP_CODE_AND_8 0x31 /* AND ($aa),Y */
|
||||
/* LSR - Logical Shift Right */
|
||||
#define OP_CODE_LSR_1 0x4a /* LSR A */
|
||||
#define OP_CODE_LSR_2 0x46 /* LSR $aa */
|
||||
#define OP_CODE_LSR_3 0x56 /* LSR $aa,X */
|
||||
#define OP_CODE_LSR_4 0x4e /* LSR $aaaa */
|
||||
#define OP_CODE_LSR_5 0x5e /* LSR $aaaa,X */
|
||||
/* ROL - Rotate Left */
|
||||
#define OP_CODE_ROL_1 0x2a /* ROL A */
|
||||
#define OP_CODE_ROL_2 0x26 /* ROL $aa */
|
||||
#define OP_CODE_ROL_3 0x36 /* ROL $aa,X */
|
||||
#define OP_CODE_ROL_4 0x2e /* ROL $aaaa */
|
||||
#define OP_CODE_ROL_5 0x3e /* ROL $aaaa,X */
|
||||
/* ROR - Rotate Right */
|
||||
#define OP_CODE_ROR_1 0x6a /* ROR A */
|
||||
#define OP_CODE_ROR_2 0x66 /* ROR $aa */
|
||||
#define OP_CODE_ROR_3 0x76 /* ROR $aa,X */
|
||||
#define OP_CODE_ROR_4 0x6e /* ROR $aaaa */
|
||||
#define OP_CODE_ROR_5 0x7e /* ROR $aaaa,X */
|
||||
/* ASL - Arithmetic Shift Left */
|
||||
#define OP_CODE_ASL_1 0x0a /* ASL A */
|
||||
#define OP_CODE_ASL_2 0x06 /* ASL $aa */
|
||||
#define OP_CODE_ASL_3 0x16 /* ASL $aa,X */
|
||||
#define OP_CODE_ASL_4 0x0e /* ASL $aaaa */
|
||||
#define OP_CODE_ASL_5 0x1e /* ASL $aaaa,X */
|
||||
#define OP_CODE_PHA 0x48 /* PHA - Push A */
|
||||
#define OP_CODE_PLA 0x68 /* PLA - Pull A (Pop) */
|
||||
#define OP_CODE_PHP 0x08 /* PHP - Push Processor Status Register */
|
||||
#define OP_CODE_PLP 0x28 /* PLP - Pull Processor Status Register */
|
||||
/* EOR - Exclusive-OR */
|
||||
#define OP_CODE_EOR_1 0x49 /* EOR #aa */
|
||||
#define OP_CODE_EOR_2 0x45 /* EOR $aa */
|
||||
#define OP_CODE_EOR_3 0x55 /* EOR $aa,X */
|
||||
#define OP_CODE_EOR_4 0x4d /* EOR $aaaa */
|
||||
#define OP_CODE_EOR_5 0x5d /* EOR $aaaa,X */
|
||||
#define OP_CODE_EOR_6 0x59 /* EOR $aaaa,Y */
|
||||
#define OP_CODE_EOR_7 0x41 /* EOR ($aa,X) */
|
||||
#define OP_CODE_EOR_8 0x51 /* EOR ($aa),Y */
|
||||
#define OP_CODE_RTS 0x60 /* RTS - Return from SubRoutine */
|
||||
#define OP_CODE_RTI 0x40 /* RTI - Return from Interrupt */
|
||||
#define OP_CODE_NOP 0xea /* NOP - No Operation */
|
||||
|
||||
#endif
|
||||
26
src/debug.c
Normal file
26
src/debug.c
Normal file
@@ -0,0 +1,26 @@
|
||||
/*----------------------------------------------------------------------------
|
||||
* debug.c:
|
||||
* Debug Function
|
||||
*----------------------------------------------------------------------------
|
||||
* nes: Nintendo Entertainment System Emulator.
|
||||
*
|
||||
* Created by Jean-Baptiste Nadal on 12/02/08.
|
||||
* Copyright 2008. All rights reserved.
|
||||
*----------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
#include "debug.h"
|
||||
|
||||
/*******************************************************
|
||||
* This function print the Trace.
|
||||
*/
|
||||
void DebugPrint (const char *format,...)
|
||||
{
|
||||
va_list ap;
|
||||
va_start(ap, format);
|
||||
vprintf (format, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
30
src/debug.h
Normal file
30
src/debug.h
Normal file
@@ -0,0 +1,30 @@
|
||||
/*----------------------------------------------------------------------------
|
||||
* debug.h:
|
||||
* Debug Function
|
||||
*----------------------------------------------------------------------------
|
||||
* nes: Nintendo Entertainment System Emulator.
|
||||
*
|
||||
* Created by Jean-Baptiste Nadal on 12/02/08.
|
||||
* Copyright 2008. All rights reserved.
|
||||
*----------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#ifndef __DEBUG_H
|
||||
#define __DEBUG_H
|
||||
|
||||
extern void DebugPrint (const char *format,...);
|
||||
|
||||
#ifdef DEBUG
|
||||
# define DEBUG_REGS(a) printRegs(a)
|
||||
# define DEBUG0(a) do { DebugPrint a; }while(0)
|
||||
# define DEBUG1(a)
|
||||
# define DEBUG2(a)
|
||||
#else
|
||||
# define DEBUG_REGS
|
||||
# define DEBUG0(a)
|
||||
# define DEBUG1(a)
|
||||
# define DEBUG2(a)
|
||||
#endif
|
||||
|
||||
|
||||
#endif
|
||||
37
src/emulator/apu.c
Normal file
37
src/emulator/apu.c
Normal file
@@ -0,0 +1,37 @@
|
||||
/*----------------------------------------------------------------------------
|
||||
* apu.c: Audio Processor Unit
|
||||
*
|
||||
*----------------------------------------------------------------------------
|
||||
* nes: Nintendo Entertainment System Emulator.
|
||||
*
|
||||
* Created by Jean-Baptiste Nadal on 12/02/08.
|
||||
* Copyright 2008. All rights reserved.
|
||||
*----------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <SDL.h>
|
||||
|
||||
#include "memory.h"
|
||||
#include "cpu6502.h"
|
||||
#include "ppu.h"
|
||||
#include "apu.h"
|
||||
#include "paddle.h"
|
||||
#include "debugger.h"
|
||||
#include "emulator.h"
|
||||
|
||||
#include "main.h"
|
||||
|
||||
#include "debug.h"
|
||||
|
||||
|
||||
/*******************************************************
|
||||
* Init the APU Module.
|
||||
*/
|
||||
int apu_init ()
|
||||
{
|
||||
int result = 0;
|
||||
|
||||
return result;
|
||||
}
|
||||
27
src/emulator/apu.h
Normal file
27
src/emulator/apu.h
Normal file
@@ -0,0 +1,27 @@
|
||||
/*----------------------------------------------------------------------------
|
||||
* apu.h: Audio Processor Unit
|
||||
*
|
||||
*----------------------------------------------------------------------------
|
||||
* nes: Nintendo Entertainment System Emulator.
|
||||
*
|
||||
* Created by Jean-Baptiste Nadal on 12/02/08.
|
||||
* Copyright 2008. All rights reserved.
|
||||
*----------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#ifndef _APU_H
|
||||
#define _APU_H
|
||||
|
||||
|
||||
#define APU_REGISTER_SIZE 0x18
|
||||
|
||||
typedef struct nes_apu_t {
|
||||
/* Register of the APU. */
|
||||
unsigned char registers [APU_REGISTER_SIZE];
|
||||
|
||||
}nes_apu_t;
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
231
src/emulator/debugger.c
Normal file
231
src/emulator/debugger.c
Normal file
@@ -0,0 +1,231 @@
|
||||
/*----------------------------------------------------------------------------
|
||||
* debugger.c: This files contain the main functions to have the debugger
|
||||
* functionality under the nes emulator.
|
||||
*
|
||||
*----------------------------------------------------------------------------
|
||||
* nes: Nintendo Entertainment System Emulator.
|
||||
*
|
||||
* Created by Jean-Baptiste Nadal on 22/02/08.
|
||||
* Copyright 2008. All rights reserved.
|
||||
*----------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <SDL.h>
|
||||
|
||||
#include "memory.h"
|
||||
#include "cpu6502.h"
|
||||
#include "ppu.h"
|
||||
#include "apu.h"
|
||||
#include "paddle.h"
|
||||
#include "debugger.h"
|
||||
#include "emulator.h"
|
||||
#include "main.h"
|
||||
|
||||
#include "debug.h"
|
||||
|
||||
|
||||
/*******************************************************
|
||||
* Put an Alert on the Register 'reg' when the value
|
||||
* 'param' is set on it.
|
||||
*/
|
||||
void PutAlertRegister (uint8_t reg, uint16_t param)
|
||||
{
|
||||
fprintf (stdout, "Put an Alert on the register %c, when the value "
|
||||
"will be: %x\n", reg, param);
|
||||
|
||||
switch (reg) {
|
||||
case 'a':
|
||||
emul.cpu.alert.a = param;
|
||||
break;
|
||||
case 'x':
|
||||
emul.cpu.alert.x = param;
|
||||
break;
|
||||
case 'y':
|
||||
emul.cpu.alert.y = param;
|
||||
break;
|
||||
default:
|
||||
fprintf (stdout, "Unknown Register: %c(%d)\n", reg, reg);
|
||||
}
|
||||
|
||||
/* Active the alert. */
|
||||
emul.cpu.alert.active = 1;
|
||||
|
||||
}
|
||||
|
||||
/*******************************************************
|
||||
* Remove an alert on the register 'reg'
|
||||
*/
|
||||
void RemoveAlert (uint8_t reg)
|
||||
{
|
||||
fprintf (stdout, "Remove the Alert for Register %c.\n", reg);
|
||||
|
||||
/* Init the Alert Register. */
|
||||
switch (reg) {
|
||||
case 'a':
|
||||
emul.cpu.alert.a = -1;
|
||||
break;
|
||||
case 'x':
|
||||
emul.cpu.alert.x = -1;
|
||||
break;
|
||||
case 'y':
|
||||
emul.cpu.alert.y = -1;
|
||||
break;
|
||||
default:
|
||||
fprintf (stdout, "Unknown Register: %c\n", reg);
|
||||
}
|
||||
|
||||
/* Remove the alert use if no more alert is active. */
|
||||
if ((emul.cpu.alert.a != -1) && (emul.cpu.alert.x != -1) &&
|
||||
(emul.cpu.alert.y != -1)) {
|
||||
emul.cpu.alert.active = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/*******************************************************
|
||||
* This function is the main function of the debugger.
|
||||
*/
|
||||
int debuggerMain (void *unused)
|
||||
{
|
||||
char buffer [50];
|
||||
uint16_t param;
|
||||
uint8_t reg;
|
||||
|
||||
/* Get the Start Address of the Rom. */
|
||||
param = memory_Read16 (0xFFFD, 0xFFFC);
|
||||
fprintf (stdout, "Rom start at: $%.4x\n", param);
|
||||
/* Get the Start Address of the NMI. */
|
||||
param = memory_Read16 (0xFFFB, 0xFFFA);
|
||||
fprintf (stdout, "NMI start at: $%.4x\n", param);
|
||||
|
||||
while (emul.quit != 0) {
|
||||
|
||||
fflush (stdin);
|
||||
fprintf (stdout, "> ");
|
||||
fgets (buffer, 50, stdin);
|
||||
/* Manage the Actions */
|
||||
switch (buffer [0]) {
|
||||
case 'h':
|
||||
fprintf (stdout, "Help:\n");
|
||||
fprintf (stdout, "=====\n");
|
||||
fprintf (stdout, " - h: Display Help.\n");
|
||||
fprintf (stdout, " - Enter key: Launch the next instruction.\n");
|
||||
fprintf (stdout, " - q: Quit the Emulator.\n");
|
||||
fprintf (stdout, " - t xxxx: Set Pc to the address xxx.\n");
|
||||
fprintf (stdout, " - = xxxx: Put a break Point at the address xxxx.\n");
|
||||
fprintf (stdout, " - m xxxx: Display the memory at the address xxxx.\n");
|
||||
fprintf (stdout, " - a var=xx: Make an alert on the register var to"
|
||||
" the value xx.\n");
|
||||
fprintf (stdout, " - s x : Show Tiles (x).\n");
|
||||
fprintf (stdout, " - s c: Clear Screen.\n");
|
||||
break;
|
||||
case 0:
|
||||
case 10:
|
||||
SDL_mutexP (emul.debugger.mutexDebugger);
|
||||
emul.debugger.debugExec = ONLY_ONE_OP;
|
||||
SDL_mutexV (emul.debugger.mutexDebugger);
|
||||
break;
|
||||
case 'q':
|
||||
fprintf (stdout, "Quit\n");
|
||||
emul.quit = 0;
|
||||
break;
|
||||
case 's':
|
||||
|
||||
if (*(buffer+2) == 'c') {
|
||||
fprintf (stdout, "Clear the PPU Screen.\n");
|
||||
PPU_ClearScreen ();
|
||||
}
|
||||
else {
|
||||
sscanf (buffer+1, "%hd", ¶m);
|
||||
PPU_ShowTiles (param);
|
||||
}
|
||||
break;
|
||||
case 'c':
|
||||
PPU_ClearScreen ();
|
||||
break;
|
||||
case 'm':
|
||||
param = 0;
|
||||
sscanf (buffer+1, "%hx", ¶m);
|
||||
DumpMemory (param);
|
||||
break;
|
||||
case 'a':
|
||||
if (strlen (buffer+1) == 4) {
|
||||
reg = buffer[3];
|
||||
RemoveAlert (reg);
|
||||
}
|
||||
else if (strlen (buffer+1) < 5) {
|
||||
fprintf (stdout, "Unknown syntax for the set alert.%d\n");
|
||||
fprintf (stdout, " exemples:\n");
|
||||
fprintf (stdout, " - a x=ff Add an alert on the register x.\n");
|
||||
fprintf (stdout, " - a rx remove the alert on the register x.\n");
|
||||
}
|
||||
else {
|
||||
reg = buffer[2];
|
||||
param = 0;
|
||||
sscanf (buffer+4, "%hx", ¶m);
|
||||
PutAlertRegister (reg, param);
|
||||
SDL_mutexP (emul.debugger.mutexDebugger);
|
||||
emul.debugger.debugExec = MANY_OPS;
|
||||
SDL_mutexV (emul.debugger.mutexDebugger);
|
||||
|
||||
}
|
||||
break;
|
||||
case 't':
|
||||
param = 0;
|
||||
/* Read the Address. */
|
||||
sscanf (buffer+1, "%hx", ¶m);
|
||||
fprintf (stdout, "Set PC to: %x\n", param);
|
||||
emul.cpu.pc = param;
|
||||
break;
|
||||
case '=':
|
||||
param = 0;
|
||||
/* Read the Address. */
|
||||
sscanf (buffer+1, "%hx", ¶m);
|
||||
fprintf (stdout, "Set Break Point at: %x\n", param);
|
||||
|
||||
SDL_mutexP (emul.debugger.mutexDebugger);
|
||||
emul.debugger.debugExec = MANY_OPS;
|
||||
emul.debugger.breakPoint = param;
|
||||
SDL_mutexV (emul.debugger.mutexDebugger);
|
||||
|
||||
break;
|
||||
default:
|
||||
fprintf (stdout, "Unknown commmand (%d).\n", buffer[0]);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*******************************************************
|
||||
* This init the debugger.
|
||||
*/
|
||||
int debugger_init ()
|
||||
{
|
||||
int result = 0;
|
||||
SDL_Thread *thread;
|
||||
|
||||
DEBUG0 (("INIT Debug\n"));
|
||||
|
||||
/* Create the Mutex for the Debugger. */
|
||||
emul.debugger.mutexDebugger = SDL_CreateMutex ();
|
||||
|
||||
thread = SDL_CreateThread (debuggerMain, NULL);
|
||||
if (thread == NULL) {
|
||||
result = -1;
|
||||
fprintf(stderr, "Unable to create thread: %s\n", SDL_GetError());
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/*******************************************************
|
||||
* This stop the execution the debugger.
|
||||
*/
|
||||
void debugger_stop ()
|
||||
{
|
||||
SDL_mutexP (emul.debugger.mutexDebugger);
|
||||
emul.debugger.debugExec = 0;
|
||||
SDL_mutexV (emul.debugger.mutexDebugger);
|
||||
}
|
||||
29
src/emulator/debugger.h
Normal file
29
src/emulator/debugger.h
Normal file
@@ -0,0 +1,29 @@
|
||||
/*----------------------------------------------------------------------------
|
||||
* debugger.h: This files contain the main functions to have the debugger
|
||||
* functionality under the nes emulator.
|
||||
*
|
||||
*----------------------------------------------------------------------------
|
||||
* nes: Nintendo Entertainment System Emulator.
|
||||
*
|
||||
* Created by Jean-Baptiste Nadal on 22/02/08.
|
||||
* Copyright 2008. All rights reserved.
|
||||
*----------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#ifndef __DEBUGGER_H
|
||||
#define __DEBUGGER_H
|
||||
|
||||
#define ONLY_ONE_OP 1
|
||||
#define MANY_OPS 2
|
||||
|
||||
# define PRINT_OP(a) if (emul.debugger.use == 1) {DebugPrint a;} else { DEBUG1 (a);}
|
||||
|
||||
typedef struct debugger_t {
|
||||
/* Mutex used to block/unblock the debugger. */
|
||||
SDL_mutex* mutexDebugger;
|
||||
uint8_t debugExec;
|
||||
uint8_t use;
|
||||
uint16_t breakPoint;
|
||||
} debugger_t;
|
||||
|
||||
#endif
|
||||
464
src/emulator/emulator.c
Normal file
464
src/emulator/emulator.c
Normal file
@@ -0,0 +1,464 @@
|
||||
/*----------------------------------------------------------------------------
|
||||
* emulator.c. Main function of the Emulator.
|
||||
*
|
||||
*----------------------------------------------------------------------------
|
||||
* nes: Nintendo Entertainment System Emulator.
|
||||
*
|
||||
* Created by Jean-Baptiste Nadal on 12/02/08.
|
||||
* Copyright 2008. All rights reserved.
|
||||
*----------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <SDL.h>
|
||||
|
||||
#include "memory.h"
|
||||
#include "cpu6502.h"
|
||||
#include "cyclesTable.h"
|
||||
#include "ppu.h"
|
||||
#include "apu.h"
|
||||
#include "paddle.h"
|
||||
#include "debugger.h"
|
||||
#include "emulator.h"
|
||||
#include "main.h"
|
||||
#include "nespal.h"
|
||||
|
||||
#include "debug.h"
|
||||
|
||||
|
||||
/* Local functions. */
|
||||
int Window_Init ();
|
||||
int HandleEvent (SDL_Event *event);
|
||||
|
||||
/* Global Emulator. */
|
||||
nes_emulator_t emul;
|
||||
|
||||
/*******************************************************
|
||||
* Init the Emulator.
|
||||
*/
|
||||
int emulator_init ()
|
||||
{
|
||||
int result = 0;
|
||||
|
||||
/* Init the variables. */
|
||||
emul.quit = 1;
|
||||
emul.cpu.nbCycles = NB_CYCLES_MAX;
|
||||
emul.nbLines = 0;
|
||||
|
||||
/* Memory Init. */
|
||||
result = memory_init ();
|
||||
if (result != 0) {
|
||||
fprintf (stderr, "Impossible to Init Memory.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* CPU Init. */
|
||||
|
||||
/* Set the Memory acces to the CPU. */
|
||||
emul.cpu.memRead = memory_Read;
|
||||
emul.cpu.memWrite = memory_Write;
|
||||
|
||||
result = cpu6502_init ();
|
||||
if (result != 0) {
|
||||
fprintf (stderr, "Impossible to Init CPU.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* PPU Init. */
|
||||
result = ppu_init ();
|
||||
if (result != 0) {
|
||||
fprintf (stderr, "Impossible to Init PPU.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* APU Init. */
|
||||
result = apu_init ();
|
||||
if (result != 0) {
|
||||
fprintf (stderr, "Impossible to Init APU.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Init the Paddles. */
|
||||
paddle_Init (&emul.Pad1);
|
||||
paddle_Init (&emul.Pad2);
|
||||
|
||||
/* SDL Init. */
|
||||
result = Window_Init ();
|
||||
if (result != 0) {
|
||||
fprintf (stderr, "Impossible to Init Window.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/*******************************************************
|
||||
* Load the Rom in the Emulator.
|
||||
*/
|
||||
int emulator_load (char *romName)
|
||||
{
|
||||
int result = 0;
|
||||
int romfd;
|
||||
int size, readSize;
|
||||
int size_prgRom, size_chrRom;
|
||||
unsigned char *romBuffer;
|
||||
uint8_t mapper, mirroring, tmp;
|
||||
fprintf (stdout, "Load <%s>\n", romName);
|
||||
|
||||
/* Open ROM file */
|
||||
romfd = open (romName, O_RDONLY);
|
||||
if (romfd < 0) {
|
||||
fprintf(stderr,"Unable to open %s\n", romName);
|
||||
}
|
||||
/* Get the Size of the file. */
|
||||
size = lseek (romfd, 0, SEEK_END);
|
||||
if (size < 0) {
|
||||
fprintf (stderr, "Unable to read %s\n",romName);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Return to the beginning of the File. */
|
||||
lseek (romfd, 0, SEEK_SET);
|
||||
|
||||
/* Allocate temporary buffer for the Rom File. */
|
||||
romBuffer = malloc (sizeof(char) * size);
|
||||
if (romBuffer == NULL) {
|
||||
fprintf (stderr, "Impossible to allocate rom buffer.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Read the Rom file. */
|
||||
readSize = read (romfd, romBuffer, size);
|
||||
if (readSize <= 0) {
|
||||
fprintf (stderr, "Impossible to read the rom file.\n");
|
||||
return readSize;
|
||||
}
|
||||
|
||||
/* Check if the open file is a NES Rom file. */
|
||||
if ((romBuffer[0] != 'N') && (romBuffer[1] != 'E') &&
|
||||
(romBuffer[2] != 'S')) {
|
||||
fprintf (stderr, "The Rom File is not a NES rom. [%d,%d,%d]\n",
|
||||
romBuffer[0], romBuffer[1], romBuffer[2]);
|
||||
result = -1;
|
||||
}
|
||||
|
||||
/* Get Section Size */
|
||||
size_prgRom = romBuffer[4];
|
||||
size_chrRom = romBuffer[5];
|
||||
|
||||
fprintf (stderr, "prgRom: %d, chrRom: %d\n", size_prgRom, size_chrRom);
|
||||
|
||||
/* Get the Mapper Number. */
|
||||
/* Get Lower part of the Mapper Number */
|
||||
tmp = romBuffer[6] >> 4;
|
||||
mapper = tmp;
|
||||
tmp = romBuffer[7] >> 4;
|
||||
mapper |= (tmp << 4);
|
||||
|
||||
/* TODO: We only support mapper0 */
|
||||
if (mapper != 0) {
|
||||
fprintf (stderr, "Unsupported Mapper %d.\n", mapper);
|
||||
return -1;
|
||||
}
|
||||
|
||||
fprintf (stdout, "Mapper: %d\n", mapper);
|
||||
/* Get the Mirroring State. */
|
||||
mirroring = romBuffer[6] & 0x1;
|
||||
|
||||
if (mirroring == 1) {
|
||||
fprintf (stdout, "Mirroring Vertical.\n");
|
||||
}
|
||||
else {
|
||||
fprintf (stdout, "Mirroring Horizontal.\n");
|
||||
}
|
||||
|
||||
/* Allocate the Rom Memory. */
|
||||
emul.memory.prgRom = malloc (MEMORY_PROGROM_SIZE);
|
||||
if (emul.memory.prgRom == NULL) {
|
||||
fprintf (stderr, "Impossible to allocate memory for Prg Rom.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Copy the program Rom. */
|
||||
if (size_prgRom == 1) {
|
||||
memcpy (emul.memory.prgRom + (MEMORY_PROGROM_SIZE / 2),
|
||||
romBuffer + 16, (1024 * 16 * size_prgRom));
|
||||
}
|
||||
else {
|
||||
memcpy (emul.memory.prgRom, romBuffer + 16, (1024 * 16 * size_prgRom));
|
||||
}
|
||||
/* Copy the Data Rom. */
|
||||
memcpy (emul.ppu.vram, romBuffer + 16 + (1024 * 16 * size_prgRom),
|
||||
(1024 * 8) * size_chrRom);
|
||||
|
||||
/* Close the Rom file. */
|
||||
close (romfd);
|
||||
|
||||
/* Finally Clear the temporary Buffer. */
|
||||
free (romBuffer);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/*******************************************************
|
||||
* This function is the Main Loop of the Emulator.
|
||||
* Execute the commands.
|
||||
* * TODO adresses:
|
||||
* - INIT $8000
|
||||
* - IRQ $FFF0
|
||||
* - NMI $8082
|
||||
*/
|
||||
int emulator_loop (int debug)
|
||||
{
|
||||
int result = 0;
|
||||
SDL_Event event;
|
||||
uint8_t opcode;
|
||||
|
||||
emul.debugger.use = debug;
|
||||
|
||||
/* Reset the Machine. */
|
||||
result = cpuReset (&emul.cpu);
|
||||
if (result != 0) {
|
||||
fprintf (stderr, "Impossible to reset the CPU.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
while (emul.quit != 0) {
|
||||
|
||||
/* BEGIN Critical Section. */
|
||||
if (debug == 1) {
|
||||
SDL_mutexP (emul.debugger.mutexDebugger);
|
||||
|
||||
/* If we put a break point. */
|
||||
if ((emul.debugger.debugExec == MANY_OPS) &&
|
||||
(emul.debugger.breakPoint == emul.cpu.pc)){
|
||||
emul.debugger.debugExec = 0;
|
||||
}
|
||||
|
||||
if (emul.debugger.debugExec > 0) {
|
||||
/* Manage the Current Opcode. */
|
||||
opcode = emul.cpu.memRead (emul.cpu.pc);
|
||||
result = cpu6502_execOpCode (&emul.cpu, opcode);
|
||||
if (result != 0) {
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (debug == 1) {
|
||||
printRegs (&emul.cpu);
|
||||
}
|
||||
|
||||
/* Compute the CPU Time taken by the executed opcode. */
|
||||
emul.cpu.nbCycles -= OpcodesCycles[opcode];
|
||||
}
|
||||
if (emul.debugger.debugExec == ONLY_ONE_OP) {
|
||||
emul.debugger.debugExec = 0;
|
||||
}
|
||||
|
||||
/* END Critical Section. */
|
||||
SDL_mutexV (emul.debugger.mutexDebugger);
|
||||
}
|
||||
else {
|
||||
|
||||
/* Manage the Current Opcode. */
|
||||
opcode = memory_fetch (emul.cpu.pc);
|
||||
result = cpu6502_execOpCode (&emul.cpu, opcode);
|
||||
if (result != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
DEBUG_REGS(&emul.cpu);
|
||||
|
||||
/* Compute the CPU Time taken by the executed opcode. */
|
||||
emul.cpu.nbCycles -= OpcodesCycles[opcode];
|
||||
}
|
||||
|
||||
if (emul.cpu.nbCycles <= 0) {
|
||||
DEBUG2 (("1 ligne.(%d)\n", emul.nbCycles));
|
||||
emul.cpu.nbCycles += NB_CYCLES_MAX;
|
||||
emul.nbLines++;
|
||||
if (emul.nbLines == 40) {
|
||||
emul.ppu.spriteHit = 0x40;
|
||||
}
|
||||
if (emul.nbLines == NES_HEIGHT) {
|
||||
/* We are now in the VBLANK. */
|
||||
/* Change the Status of the */
|
||||
bitsetPPUStatus (7, 1);
|
||||
|
||||
/* Before Launch the NMI. we Redraw the Screen. */
|
||||
PPU_RedrawNesScreen ();
|
||||
|
||||
/* Launch NMI */
|
||||
NMI_Interrupt (&emul.cpu);
|
||||
|
||||
}
|
||||
if (emul.nbLines == (NES_HEIGHT + NB_LINES_VBLANK)) {
|
||||
/* VBLANK is finish, we restart at the First line. */
|
||||
emul.nbLines = 0;
|
||||
emul.ppu.spriteHit = 0;
|
||||
bitsetPPUStatus (7, 0);
|
||||
}
|
||||
}
|
||||
|
||||
/* Manage the SDL Event. */
|
||||
while (SDL_PollEvent (&event)) {
|
||||
HandleEvent (&event);
|
||||
}
|
||||
}
|
||||
|
||||
/* Quit the SDL program. */
|
||||
SDL_Quit();
|
||||
|
||||
DEBUG0 (("quit: %d\n", emul.quit));
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************
|
||||
* This function is the init function of the Window.
|
||||
*/
|
||||
int Window_Init ()
|
||||
{
|
||||
int result, i;
|
||||
SDL_Color colors[256];
|
||||
|
||||
result = SDL_Init (SDL_INIT_TIMER|SDL_INIT_AUDIO|SDL_INIT_VIDEO);
|
||||
if (result != 0) {
|
||||
fprintf (stderr, "Impossible to Init Window.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Set video mode */
|
||||
emul.ppu.screen = SDL_SetVideoMode (NES_WIDTH, NES_HEIGHT, 8, SDL_SWSURFACE);
|
||||
if (! emul.ppu.screen) {
|
||||
fprintf (stderr, "Couldn't set %dx%d video mode: %s\n",
|
||||
NES_WIDTH, NES_HEIGHT, SDL_GetError());
|
||||
return -2;
|
||||
}
|
||||
|
||||
/* Init the Palette of the Emulator. */
|
||||
for (i=0; i < 256; i++) {
|
||||
/* Step1: Init the Palette.*/
|
||||
colors[i].r=0;
|
||||
colors[i].g=0;
|
||||
colors[i].b=0;
|
||||
}
|
||||
|
||||
for (i=0; i < 64; i++) {
|
||||
/* Step1: Init the Palette.*/
|
||||
colors[i].r=nes_palette[i].r;
|
||||
colors[i].g=nes_palette[i].g;
|
||||
colors[i].b=nes_palette[i].b;
|
||||
}
|
||||
|
||||
/* Set palette */
|
||||
SDL_SetPalette(emul.ppu.screen, SDL_LOGPAL|SDL_PHYSPAL, colors, 0, 256);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*******************************************************
|
||||
* This function is the Main SDL Event function.
|
||||
*/
|
||||
int HandleEvent (SDL_Event *event)
|
||||
{
|
||||
|
||||
switch (event->type) {
|
||||
|
||||
case SDL_KEYDOWN:
|
||||
/* Manage the different keys. */
|
||||
switch (event->key.keysym.sym) {
|
||||
|
||||
case SDLK_ESCAPE:
|
||||
emul.quit = 0;
|
||||
break;
|
||||
case SDLK_UP:
|
||||
DEBUG0 (("UP pressed.\n"));
|
||||
emul.Pad1.keys [KEY_UP] = 1;
|
||||
break;
|
||||
case SDLK_DOWN:
|
||||
DEBUG0 (("DOWN pressed.\n"));
|
||||
emul.Pad1.keys [KEY_DOWN] = 1;
|
||||
break;
|
||||
case SDLK_RIGHT:
|
||||
DEBUG0 (("RIGHT pressed.\n"));
|
||||
emul.Pad1.keys [KEY_RIGHT] = 1;
|
||||
break;
|
||||
case SDLK_LEFT:
|
||||
DEBUG0 (("LEFT pressed.\n"));
|
||||
emul.Pad1.keys [KEY_LEFT] = 1;
|
||||
break;
|
||||
case SDLK_LCTRL:
|
||||
DEBUG0 (("LEFT CONTROL pressed.\n"));
|
||||
emul.Pad1.keys [KEY_A] = 1;
|
||||
break;
|
||||
case SDLK_LALT:
|
||||
DEBUG0 (("LEFT ALT pressed.\n"));
|
||||
emul.Pad1.keys [KEY_B] = 1;
|
||||
break;
|
||||
case SDLK_RETURN:
|
||||
DEBUG0 (("RETURN pressed.\n"));
|
||||
emul.Pad1.keys [KEY_START] = 1;
|
||||
break;
|
||||
case SDLK_BACKSPACE:
|
||||
DEBUG0 (("BACKSPACE pressed.\n"));
|
||||
emul.Pad1.keys [KEY_SELECT] = 1;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case SDL_KEYUP:
|
||||
/* Manage the different keys. */
|
||||
switch (event->key.keysym.sym) {
|
||||
|
||||
case SDLK_UP:
|
||||
DEBUG0 (("UP UnPressed.\n"));
|
||||
emul.Pad1.keys [KEY_UP] = 0;
|
||||
break;
|
||||
case SDLK_DOWN:
|
||||
DEBUG0 (("DOWN UnPressed.\n"));
|
||||
emul.Pad1.keys [KEY_DOWN] = 0;
|
||||
break;
|
||||
case SDLK_RIGHT:
|
||||
DEBUG0 (("RIGHT UnPressed.\n"));
|
||||
emul.Pad1.keys [KEY_RIGHT] = 0;
|
||||
break;
|
||||
case SDLK_LEFT:
|
||||
DEBUG0 (("LEFT UnPressed.\n"));
|
||||
emul.Pad1.keys [KEY_LEFT] = 0;
|
||||
break;
|
||||
case SDLK_LCTRL:
|
||||
DEBUG0 (("LEFT CONTROL UnPressed.\n"));
|
||||
emul.Pad1.keys [KEY_A] = 0;
|
||||
break;
|
||||
case SDLK_LALT:
|
||||
DEBUG0 (("LEFT ALT UnPressed.\n"));
|
||||
emul.Pad1.keys [KEY_B] = 0;
|
||||
break;
|
||||
case SDLK_RETURN:
|
||||
DEBUG0 (("RETURN UnPressed.\n"));
|
||||
emul.Pad1.keys [KEY_START] = 0;
|
||||
break;
|
||||
case SDLK_BACKSPACE:
|
||||
DEBUG0 (("BACKSPACE UnPressed.\n"));
|
||||
emul.Pad1.keys [KEY_SELECT] = 0;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case SDL_QUIT:
|
||||
emul.quit = 0;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
49
src/emulator/emulator.h
Normal file
49
src/emulator/emulator.h
Normal file
@@ -0,0 +1,49 @@
|
||||
/*----------------------------------------------------------------------------
|
||||
* emulator.h. Emulator Object structure.
|
||||
*
|
||||
*----------------------------------------------------------------------------
|
||||
* nes: Nintendo Entertainment System Emulator.
|
||||
*
|
||||
* Created by Jean-Baptiste Nadal on 12/02/08.
|
||||
* Copyright 2008. All rights reserved.
|
||||
*----------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
|
||||
#ifndef _EMULATOR_H
|
||||
#define _EMULATOR_H
|
||||
|
||||
/* Number of cycle to make a complete line. */
|
||||
#define NB_CYCLES_MAX 140
|
||||
|
||||
/* Number of line for the VBLANK. */
|
||||
#define NB_LINES_VBLANK 70
|
||||
|
||||
typedef struct nes_emulator_t {
|
||||
/* Memory. */
|
||||
nes_memory_t memory;
|
||||
/* CPU. */
|
||||
nes_cpu6502_t cpu;
|
||||
/* PPU. */
|
||||
nes_ppu_t ppu;
|
||||
/* APU */
|
||||
nes_apu_t apu;
|
||||
|
||||
/* Debugger. */
|
||||
debugger_t debugger;
|
||||
|
||||
/* Paddles. */
|
||||
paddle_t Pad1;
|
||||
paddle_t Pad2;
|
||||
|
||||
/* Counter of Lines to know where we are. */
|
||||
int16_t nbLines;
|
||||
|
||||
/* Boolean to quit the emulator. */
|
||||
uint8_t quit;
|
||||
}nes_emulator_t;
|
||||
|
||||
|
||||
int SDLCALL SDLEventLoop (void *data);
|
||||
|
||||
#endif
|
||||
259
src/emulator/memory.c
Normal file
259
src/emulator/memory.c
Normal file
@@ -0,0 +1,259 @@
|
||||
/*----------------------------------------------------------------------------
|
||||
* memory.c. Main function to manage memory.
|
||||
*
|
||||
*----------------------------------------------------------------------------
|
||||
* nes: Nintendo Entertainment System Emulator.
|
||||
*
|
||||
* Created by Jean-Baptiste Nadal on 12/02/08.
|
||||
* Copyright 2008. All rights reserved.
|
||||
*----------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h> /* for exit */
|
||||
|
||||
#include <SDL.h>
|
||||
|
||||
#include "memory.h"
|
||||
#include "cpu6502.h"
|
||||
#include "ppu.h"
|
||||
#include "apu.h"
|
||||
#include "paddle.h"
|
||||
#include "debugger.h"
|
||||
#include "emulator.h"
|
||||
#include "main.h"
|
||||
|
||||
#include "debug.h"
|
||||
|
||||
/*******************************************************
|
||||
* Init the Memory structure.
|
||||
*/
|
||||
int memory_init ()
|
||||
{
|
||||
int result = 0;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < MEMORY_RAM_SIZE; i++) {
|
||||
emul.memory.segment1 [i] = 0;
|
||||
}
|
||||
|
||||
//emul.memory
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/*******************************************************
|
||||
* This function read a 16bits and return it.
|
||||
*/
|
||||
uint16_t memory_Read16 (uint16_t hight, uint16_t low)
|
||||
{
|
||||
uint16_t read_value;
|
||||
/* Step1 detect the memory zone we want to access. */
|
||||
if (hight > MEMORY_PROGROM) {
|
||||
DEBUG2 (("Access PROG ROM.\n"));
|
||||
/* -Low part. */
|
||||
read_value = emul.memory.prgRom [low - MEMORY_PROGROM];
|
||||
/* -Hight part. */
|
||||
read_value |= emul.memory.prgRom [hight - MEMORY_PROGROM] << 8;
|
||||
|
||||
return read_value;
|
||||
}
|
||||
else if (hight > MEMORY_SAVESLOT_2) {
|
||||
DEBUG0 (("%d)TODO Access to Save Slot 2.\n", __LINE__));
|
||||
exit (1);
|
||||
}
|
||||
else if (hight > MEMORY_SAVESLOT_1) {
|
||||
DEBUG0 (("%d)Access to Save Slot 1.\n", __LINE__));
|
||||
exit (1);
|
||||
}
|
||||
else if (hight > MEMORY_APU_INPUT) {
|
||||
DEBUG0 (("%d)Access to APU Input.\n", __LINE__));
|
||||
exit (1);
|
||||
}
|
||||
else if (hight > MEMORY_PPU_REG) {
|
||||
DEBUG0 (("%d)Access to PPU Reg.\n", __LINE__));
|
||||
exit (1);
|
||||
}
|
||||
else if (hight > MEMORY_RAM) {
|
||||
DEBUG0 (("%d)Access to Ram.(0x%x)\n", __LINE__, hight));
|
||||
exit (1);
|
||||
}
|
||||
else {
|
||||
DEBUG2 (("Impossible to access to this Memory: 0x%x.\n", hight));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*******************************************************
|
||||
* This function return the opcode to do.
|
||||
*/
|
||||
uint8_t memory_fetch (uint16_t addr)
|
||||
{
|
||||
if (addr < MEMORY_PROGROM) {
|
||||
return memory_Read (addr);
|
||||
}
|
||||
else {
|
||||
return emul.memory.prgRom [addr - MEMORY_PROGROM];
|
||||
}
|
||||
}
|
||||
|
||||
/*******************************************************
|
||||
* This function read the memory.
|
||||
*/
|
||||
uint8_t memory_Read (uint16_t addr)
|
||||
{
|
||||
if (addr >= MEMORY_PROGROM) {
|
||||
DEBUG2 (("Access PROG ROM.\n"));
|
||||
return emul.memory.prgRom [addr - MEMORY_PROGROM];
|
||||
}
|
||||
else if (addr >= MEMORY_SAVESLOT_2) {
|
||||
DEBUG0 (("%d)TODO Access to Save Slot 2.\n", __LINE__));
|
||||
exit (1);
|
||||
}
|
||||
else if (addr >= MEMORY_SAVESLOT_1) {
|
||||
DEBUG0 (("%d)TODO Access to Save Slot 1.\n", __LINE__));
|
||||
exit (1);
|
||||
}
|
||||
else if (addr >= MEMORY_APU_INPUT) {
|
||||
DEBUG2 (("Access to APU Input (%d).\n", (addr - MEMORY_APU_INPUT)));
|
||||
|
||||
switch (addr) {
|
||||
|
||||
case 0x4016:
|
||||
return paddle_Read (&emul.Pad1);
|
||||
break;
|
||||
case 0x4017:
|
||||
return paddle_Read (&emul.Pad2);
|
||||
break;
|
||||
default:
|
||||
return emul.apu.registers[addr - MEMORY_APU_INPUT];
|
||||
}
|
||||
}
|
||||
else if (addr >= MEMORY_PPU_REG) {
|
||||
DEBUG2 (("Access to PPU Reg.\n", __LINE__));
|
||||
return memory_Read_PPu (addr);
|
||||
}
|
||||
else {
|
||||
/* if (addr >= MEMORY_RAM) */
|
||||
DEBUG2 (("%d)Access to Ram.(0x%x)\n", __LINE__, addr));
|
||||
if (addr > 0x800) {
|
||||
DEBUG0 (("TODO Access ghost RAM.(0x%x)\n", addr));
|
||||
exit (1);
|
||||
}
|
||||
|
||||
return emul.memory.segment1 [addr];
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*******************************************************
|
||||
* This function write the memory.
|
||||
*/
|
||||
void memory_Write (uint16_t addr, uint8_t value)
|
||||
{
|
||||
|
||||
if (addr < MEMORY_PPU_REG) {
|
||||
DEBUG2 (("Access to Ram.(0x%x)\n", addr));
|
||||
|
||||
emul.memory.segment1 [(addr & 0x7FF)] = value;
|
||||
DEBUG2 (("On viend d'enregistrer a %x = %x\n", (addr & 0x7FF), value));
|
||||
}
|
||||
else if (addr < MEMORY_APU_INPUT) {
|
||||
DEBUG2 (("Access to PPU Reg.\n"));
|
||||
|
||||
memory_Write_PPu (addr, value);
|
||||
}
|
||||
else if (addr < MEMORY_SAVESLOT_1) {
|
||||
switch (addr) {
|
||||
|
||||
case 0x4014:
|
||||
/* Dma Access to the Sprite memory. */
|
||||
PPU_DMA_SpriteCopy (value);
|
||||
break;
|
||||
case 0x4016:
|
||||
paddle_Write (&emul.Pad1, value);
|
||||
break;
|
||||
case 0x4017:
|
||||
paddle_Write (&emul.Pad2, value);
|
||||
break;
|
||||
default:
|
||||
fprintf (stdout, "APU input unknown: %d\n", addr);
|
||||
}
|
||||
|
||||
DEBUG2 (("Access to APU Input.\n"));
|
||||
DEBUG2 (("APU Save at %d.\n", (addr - MEMORY_APU_INPUT) ));
|
||||
emul.apu.registers[addr - MEMORY_APU_INPUT] = value;
|
||||
}
|
||||
else if (addr < MEMORY_SAVESLOT_2) {
|
||||
DEBUG0 (("TODO Access to Save Slot 1.\n"));
|
||||
exit (1);
|
||||
}
|
||||
else if (addr < MEMORY_PROGROM) {
|
||||
DEBUG0 (("TODO Access to Save Slot 2.\n"));
|
||||
exit (1);
|
||||
}
|
||||
else {
|
||||
DEBUG0 (("Impossible to write to this Memory: 0x%x.\n", addr));
|
||||
}
|
||||
}
|
||||
|
||||
/*******************************************************
|
||||
* This function read the memory in the PAGE 0.
|
||||
*/
|
||||
uint8_t memory_ReadIndirectIndexed (uint8_t addr)
|
||||
{
|
||||
return emul.memory.segment1 [MEMORY_INDIRECT_INDEXED_START + addr];
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************
|
||||
* This function write the memory in the PAGE 0.
|
||||
*/
|
||||
void memory_WriteIndirectIndexed (uint8_t addr, uint8_t value)
|
||||
{
|
||||
emul.memory.segment1 [MEMORY_INDIRECT_INDEXED_START + addr] = value;
|
||||
DEBUG2 (("WII, On viend d'enregistrer a %x = %d\n",
|
||||
(MEMORY_INDIRECT_INDEXED_START + addr), value));
|
||||
//DumpMemory ();
|
||||
}
|
||||
|
||||
/*******************************************************
|
||||
* Dump Memory.
|
||||
*/
|
||||
void DumpMemory (uint16_t StartAddr)
|
||||
{
|
||||
int i;
|
||||
uint16_t cpt = 0;
|
||||
int quit = 0;
|
||||
fprintf (stderr, "startAddr: %x\n", StartAddr);
|
||||
/* Sanity checks. */
|
||||
if (StartAddr > 0x800) {
|
||||
fprintf (stderr, "Impossible to Dump this adress (0x%x). Out of Range.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
cpt = StartAddr;
|
||||
|
||||
while (quit == 0) {
|
||||
|
||||
fprintf (stdout, "%4.4x:", cpt);
|
||||
for (i = 0; i < 16; i++) {
|
||||
if (cpt >= MEMORY_RAM_SIZE) {
|
||||
/* We are arrived at the end of the RAM. */
|
||||
quit = 1;
|
||||
break;
|
||||
}
|
||||
if (emul.memory.segment1[cpt] != 0) {
|
||||
fprintf (stdout, " \033[32m%2.2x\033[0m", emul.memory.segment1[cpt]);
|
||||
}
|
||||
else {
|
||||
fprintf (stdout, " %2.2x", emul.memory.segment1[cpt]);
|
||||
}
|
||||
|
||||
cpt++;
|
||||
}
|
||||
fprintf (stdout, "\n");
|
||||
}
|
||||
}
|
||||
56
src/emulator/memory.h
Normal file
56
src/emulator/memory.h
Normal file
@@ -0,0 +1,56 @@
|
||||
/*----------------------------------------------------------------------------
|
||||
* memory.h. memory structure.
|
||||
*
|
||||
*----------------------------------------------------------------------------
|
||||
* nes: Nintendo Entertainment System Emulator.
|
||||
*
|
||||
* Created by Jean-Baptiste Nadal on 12/02/08.
|
||||
* Copyright 2008. All rights reserved.
|
||||
*----------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#ifndef __MEMORY_H
|
||||
#define __MEMORY_H
|
||||
|
||||
|
||||
/*
|
||||
* Memory Map:
|
||||
* ==========
|
||||
* +----------+-------+-------+----------------------+
|
||||
* | Address | End | Size | Description |
|
||||
* +----------+-------+-------+----------------------+
|
||||
* | $0000 | $1FFF | $0800 | Ram |
|
||||
* | $2000 | $3FFF | $0008 | PPU Register |
|
||||
* | $4000 | $4017 | $0018 | APU & Input Register |
|
||||
* | $4018 | $5777 | $1FE8 | Written to cart * 1 |
|
||||
* | $6000 | $7FFF | $2000 | Written to cart * 2 |
|
||||
* | $8000 | $FFFF | $8000 | Program Rom * 2 |
|
||||
* +----------+-------+-------+----------------------+
|
||||
*
|
||||
*/
|
||||
|
||||
#define MEMORY_RAM 0x0000
|
||||
#define MEMORY_PPU_REG 0x2000
|
||||
#define MEMORY_APU_INPUT 0x4000
|
||||
#define MEMORY_SAVESLOT_1 0x4018
|
||||
#define MEMORY_SAVESLOT_2 0x6000
|
||||
#define MEMORY_PROGROM 0x8000
|
||||
|
||||
#define MEMORY_INDIRECT_INDEXED_START 0x00aa
|
||||
|
||||
#define MEMORY_STACK_INDEX_START 0x100
|
||||
|
||||
#define MEMORY_RAM_SIZE 2048
|
||||
#define MEMORY_PROGROM_SIZE 32768
|
||||
|
||||
typedef struct nes_memory_t {
|
||||
/* OOO - 0x800 */
|
||||
unsigned char segment1[MEMORY_RAM_SIZE];
|
||||
|
||||
/* Prg-ROM */
|
||||
unsigned char *prgRom;
|
||||
|
||||
}nes_memory_t;
|
||||
|
||||
|
||||
#endif
|
||||
46
src/emulator/nespal.h
Normal file
46
src/emulator/nespal.h
Normal file
@@ -0,0 +1,46 @@
|
||||
/*----------------------------------------------------------------------------
|
||||
* nespal.h. Contain the complete nes palette.
|
||||
*
|
||||
*----------------------------------------------------------------------------
|
||||
* nes: Nintendo Entertainment System Emulator.
|
||||
*
|
||||
* Created by Jean-Baptiste Nadal on 12/02/08.
|
||||
* Copyright 2008. All rights reserved.
|
||||
*----------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#ifndef __NESPAL_H
|
||||
#define __NESPAL_H
|
||||
|
||||
|
||||
typedef struct nescol_t {
|
||||
uint8_t r;
|
||||
uint8_t g;
|
||||
uint8_t b;
|
||||
}nescol_t;
|
||||
|
||||
|
||||
nescol_t nes_palette[64] =
|
||||
{
|
||||
{0x80,0x80,0x80}, {0x00,0x00,0xBB}, {0x37,0x00,0xBF}, {0x84,0x00,0xA6},
|
||||
{0xBB,0x00,0x6A}, {0xB7,0x00,0x1E}, {0xB3,0x00,0x00}, {0x91,0x26,0x00},
|
||||
{0x7B,0x2B,0x00}, {0x00,0x3E,0x00}, {0x00,0x48,0x0D}, {0x00,0x3C,0x22},
|
||||
{0x00,0x2F,0x66}, {0x00,0x00,0x00}, {0x05,0x05,0x05}, {0x05,0x05,0x05},
|
||||
|
||||
{0xC8,0xC8,0xC8}, {0x00,0x59,0xFF}, {0x44,0x3C,0xFF}, {0xB7,0x33,0xCC},
|
||||
{0xFF,0x33,0xAA}, {0xFF,0x37,0x5E}, {0xFF,0x37,0x1A}, {0xD5,0x4B,0x00},
|
||||
{0xC4,0x62,0x00}, {0x3C,0x7B,0x00}, {0x1E,0x84,0x15}, {0x00,0x95,0x66},
|
||||
{0x00,0x84,0xC4}, {0x11,0x11,0x11}, {0x09,0x09,0x09}, {0x09,0x09,0x09},
|
||||
|
||||
{0xFF,0xFF,0xFF}, {0x00,0x95,0xFF}, {0x6F,0x84,0xFF}, {0xD5,0x6F,0xFF},
|
||||
{0xFF,0x77,0xCC}, {0xFF,0x6F,0x99}, {0xFF,0x7B,0x59}, {0xFF,0x91,0x5F},
|
||||
{0xFF,0xA2,0x33}, {0xA6,0xBF,0x00}, {0x51,0xD9,0x6A}, {0x4D,0xD5,0xAE},
|
||||
{0x00,0xD9,0xFF}, {0x66,0x66,0x66}, {0x0D,0x0D,0x0D}, {0x0D,0x0D,0x0D},
|
||||
|
||||
{0xFF,0xFF,0xFF}, {0x84,0xBF,0xFF}, {0xBB,0xBB,0xFF}, {0xD0,0xBB,0xFF},
|
||||
{0xFF,0xBF,0xEA}, {0xFF,0xBF,0xCC}, {0xFF,0xC4,0xB7}, {0xFF,0xCC,0xAE},
|
||||
{0xFF,0xD9,0xA2}, {0xCC,0xE1,0x99}, {0xAE,0xEE,0xB7}, {0xAA,0xF7,0xEE},
|
||||
{0xB3,0xEE,0xFF}, {0xDD,0xDD,0xDD}, {0x11,0x11,0x11}, {0x11,0x11,0x11}
|
||||
};
|
||||
|
||||
#endif
|
||||
81
src/emulator/paddle.c
Normal file
81
src/emulator/paddle.c
Normal file
@@ -0,0 +1,81 @@
|
||||
/*----------------------------------------------------------------------------
|
||||
* paddle.c. Paddle Manager.
|
||||
*
|
||||
*----------------------------------------------------------------------------
|
||||
* nes: Nintendo Entertainment System Emulator.
|
||||
*
|
||||
* Created by Jean-Baptiste Nadal on 20/03/08.
|
||||
* Copyright 2008. All rights reserved.
|
||||
*----------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include <SDL.h>
|
||||
|
||||
#include "memory.h"
|
||||
#include "cpu6502.h"
|
||||
#include "ppu.h"
|
||||
#include "apu.h"
|
||||
#include "paddle.h"
|
||||
#include "debugger.h"
|
||||
#include "emulator.h"
|
||||
#include "main.h"
|
||||
|
||||
#include "debug.h"
|
||||
|
||||
|
||||
|
||||
/*******************************************************
|
||||
* Init the paddle structure.
|
||||
*/
|
||||
int paddle_Init (paddle_t *paddle)
|
||||
{
|
||||
int i;
|
||||
|
||||
paddle->lastWrite = 0;
|
||||
paddle->CurrentPosition = 1;
|
||||
|
||||
for (i = 0; i < PADDLE_NB_KEYS; i++) {
|
||||
paddle->keys[i] = 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************
|
||||
* Read the Paddle Value.
|
||||
*/
|
||||
int paddle_Read (paddle_t *paddle)
|
||||
{
|
||||
int result = 0x40;
|
||||
|
||||
if (paddle->keys[paddle->CurrentPosition] == 1) {
|
||||
result = 0x41;
|
||||
}
|
||||
|
||||
if (paddle->CurrentPosition == PADDLE_NB_KEYS) {
|
||||
paddle->CurrentPosition = 1;
|
||||
}
|
||||
else {
|
||||
paddle->CurrentPosition++;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/*******************************************************
|
||||
* Write the Paddle Value.
|
||||
*/
|
||||
void paddle_Write (paddle_t *paddle, uint8_t value)
|
||||
{
|
||||
if ((paddle->lastWrite == 1) && (value == 0)) {
|
||||
paddle_Init (paddle);
|
||||
}
|
||||
|
||||
paddle->lastWrite = value;
|
||||
}
|
||||
|
||||
39
src/emulator/paddle.h
Normal file
39
src/emulator/paddle.h
Normal file
@@ -0,0 +1,39 @@
|
||||
/*----------------------------------------------------------------------------
|
||||
* paddle.h. Paddle Manager.
|
||||
*
|
||||
*----------------------------------------------------------------------------
|
||||
* nes: Nintendo Entertainment System Emulator.
|
||||
*
|
||||
* Created by Jean-Baptiste Nadal on 20/03/08.
|
||||
* Copyright 2008. All rights reserved.
|
||||
*----------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#ifndef _PADDLE_H
|
||||
#define _PADDLE_H
|
||||
|
||||
|
||||
#define PADDLE_NB_KEYS 24
|
||||
|
||||
#define KEY_A 1
|
||||
#define KEY_B 2
|
||||
#define KEY_SELECT 3
|
||||
#define KEY_START 4
|
||||
#define KEY_UP 5
|
||||
#define KEY_DOWN 6
|
||||
#define KEY_LEFT 7
|
||||
#define KEY_RIGHT 8
|
||||
|
||||
|
||||
/* Structure of the Paddle. */
|
||||
typedef struct paddle_t {
|
||||
|
||||
uint8_t lastWrite;
|
||||
uint8_t CurrentPosition;
|
||||
|
||||
uint8_t keys[PADDLE_NB_KEYS];
|
||||
|
||||
} paddle_t;
|
||||
|
||||
|
||||
#endif /* _PADDLE_H */
|
||||
442
src/emulator/ppu.c
Normal file
442
src/emulator/ppu.c
Normal file
@@ -0,0 +1,442 @@
|
||||
/*----------------------------------------------------------------------------
|
||||
* ppu.c. Main Picture Processor.
|
||||
*
|
||||
*----------------------------------------------------------------------------
|
||||
* nes: Nintendo Entertainment System Emulator.
|
||||
*
|
||||
* Created by Jean-Baptiste Nadal on 12/02/08.
|
||||
* Copyright 2008. All rights reserved.
|
||||
*----------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include <SDL.h>
|
||||
|
||||
#include "memory.h"
|
||||
#include "cpu6502.h"
|
||||
#include "ppu.h"
|
||||
#include "apu.h"
|
||||
#include "paddle.h"
|
||||
#include "debugger.h"
|
||||
#include "emulator.h"
|
||||
#include "ppu_tiles.h"
|
||||
#include "main.h"
|
||||
|
||||
#include "debug.h"
|
||||
|
||||
|
||||
/*******************************************************
|
||||
* Init the PPU structure.
|
||||
*/
|
||||
int ppu_init ()
|
||||
{
|
||||
int result = 0;
|
||||
|
||||
emul.ppu.init = 0;
|
||||
|
||||
|
||||
/* Init the 8 registers. */
|
||||
/* ==================== */
|
||||
|
||||
/* PPU Control Register 1. */
|
||||
emul.ppu.registers.ControlRegister1 = 0;
|
||||
/* PPU Control Register 2. */
|
||||
emul.ppu.registers.ControlRegister2 = 0;
|
||||
/* PPU Status Register. */
|
||||
emul.ppu.registers.StatusRegister = 0;
|
||||
/* PPU Screen Scroll Offset. */
|
||||
emul.ppu.registers.ScreenScrollOffset = 0;
|
||||
|
||||
/* PPU Memory Address. */
|
||||
emul.ppu.registers.PPUMemAddress = 0;
|
||||
emul.ppu.registers.accessPPUMem = ACCESS_HIGH;
|
||||
|
||||
/* PPU Sprite Memory Address. */
|
||||
emul.ppu.registers.SpriteMemAddress = 0;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/*******************************************************
|
||||
* This function read the Vram.
|
||||
*/
|
||||
uint8_t PPU_Read_Vram (uint16_t addr)
|
||||
{
|
||||
/* Plage of the Memory Space. */
|
||||
addr &= 0x3FFF;
|
||||
|
||||
if ((addr & 0xFF00) == 0x3F00) {
|
||||
return emul.ppu.vram [(addr & 0xFF1F)];
|
||||
}
|
||||
else {
|
||||
return emul.ppu.vram [addr];
|
||||
}
|
||||
}
|
||||
|
||||
/*******************************************************
|
||||
* This function write the Vram.
|
||||
*/
|
||||
void PPU_Write_Vram (uint16_t addr, uint8_t value)
|
||||
{
|
||||
/* Plage of the Memory Space. */
|
||||
addr &= 0x3FFF;
|
||||
|
||||
/* If we write the first element of the palette, we mirrored it to the
|
||||
* both palette. */
|
||||
if ((addr & 0xFF00) == 0x3F00) {
|
||||
/* Palette. */
|
||||
|
||||
if ((addr == SPRITE_PALETTE) || (addr == PICTURE_PALETTE)) {
|
||||
emul.ppu.vram[SPRITE_PALETTE] = value;
|
||||
emul.ppu.vram[PICTURE_PALETTE] = value;
|
||||
}
|
||||
else {
|
||||
emul.ppu.vram[(addr & 0xFF1F)] = value;
|
||||
}
|
||||
}
|
||||
else {
|
||||
emul.ppu.vram[addr] = value;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************
|
||||
* This function read Register Value
|
||||
*/
|
||||
uint8_t memory_Read_PPu (uint16_t addr)
|
||||
{
|
||||
uint8_t result = 0;
|
||||
DEBUG2 (("%s: %x\n", __FUNCTION__, addr));
|
||||
|
||||
switch (addr) {
|
||||
|
||||
case 0x2000:
|
||||
/* PPU Control Register 1 */
|
||||
result = emul.ppu.registers.ControlRegister1;
|
||||
break;
|
||||
case 0x2001:
|
||||
/* PPU Control Register 2 */
|
||||
result = emul.ppu.registers.ControlRegister2;
|
||||
break;
|
||||
case 0x2002:
|
||||
/* PPU Status Register */
|
||||
result = emul.ppu.registers.StatusRegister | emul.ppu.spriteHit;
|
||||
emul.ppu.registers.StatusRegister = 0;
|
||||
emul.ppu.registers.PPUMemAddress = ACCESS_HIGH;
|
||||
break;
|
||||
case 0x2004:
|
||||
/* Sprite Memory Data */
|
||||
result = emul.ppu.sprite_ram [emul.ppu.registers.SpriteMemAddress];
|
||||
break;
|
||||
case 0x2007:
|
||||
/* PPU memory Data */
|
||||
/* Return the last value readed. The first time the value is unknown. */
|
||||
|
||||
if (emul.ppu.registers.PPUMemAddress < PICTURE_PALETTE) {
|
||||
result = emul.ppu.PPU_transfert;
|
||||
emul.ppu.PPU_transfert = PPU_Read_Vram (emul.ppu.registers.PPUMemAddress);
|
||||
}
|
||||
else {
|
||||
result = PPU_Read_Vram (emul.ppu.registers.PPUMemAddress);
|
||||
emul.ppu.PPU_transfert = PPU_Read_Vram
|
||||
((emul.ppu.registers.PPUMemAddress & 0x00FF) | 0x2F00);
|
||||
}
|
||||
|
||||
|
||||
if ((emul.ppu.registers.PPUMemAddress >= PATTERN_TABLE_1) &&
|
||||
(emul.ppu.registers.PPUMemAddress < NAME_TABLE_0)) {
|
||||
fprintf (stdout, "Read 2007:%x=%x\n", emul.ppu.registers.PPUMemAddress,
|
||||
result);
|
||||
}
|
||||
|
||||
/* And Increment the mem Address pointer. */
|
||||
if (emul.ppu.registers.ControlRegister1 & CONTROL_INC) {
|
||||
emul.ppu.registers.PPUMemAddress += 32;
|
||||
}
|
||||
else {
|
||||
emul.ppu.registers.PPUMemAddress++;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
DEBUG0 (("Impossible to read at this address (0x%x)\n", addr));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/*******************************************************
|
||||
* This function read Register Value
|
||||
*/
|
||||
void memory_Write_PPu (uint16_t addr, uint8_t value)
|
||||
{
|
||||
DEBUG2 (("%s:0x%x=0x%x\n", __FUNCTION__, addr, value));
|
||||
|
||||
switch (addr) {
|
||||
|
||||
case 0x2000:
|
||||
/* PPU Control Register 1 */
|
||||
emul.ppu.registers.ControlRegister1 = value;
|
||||
fprintf (stdout, "Ecriture 2000 register1: $%x\n",
|
||||
emul.ppu.registers.ControlRegister1);
|
||||
break;
|
||||
case 0x2001:
|
||||
/* PPU Control Register 2 */
|
||||
emul.ppu.registers.ControlRegister2 = value;
|
||||
break;
|
||||
case 0x2003:
|
||||
/* Sprite Memory Address */
|
||||
emul.ppu.registers.SpriteMemAddress = value;
|
||||
break;
|
||||
case 0x2004:
|
||||
/* Sprite Memory Data */
|
||||
emul.ppu.sprite_ram [emul.ppu.registers.SpriteMemAddress] = value;
|
||||
emul.ppu.registers.SpriteMemAddress++;
|
||||
break;
|
||||
case 0x2005:
|
||||
/* Screen Scroll Offset */
|
||||
DEBUG0 (("TODO: Write PPU reg: 0x2005\n"));
|
||||
|
||||
emul.ppu.registers.accessPPUMem = !emul.ppu.registers.accessPPUMem;
|
||||
//TODO exit (1);
|
||||
break;
|
||||
case 0x2006:
|
||||
/* PPU memory Address */
|
||||
if (emul.ppu.registers.accessPPUMem) {
|
||||
emul.ppu.registers.PPUMemAddress =
|
||||
(emul.ppu.registers.PPUMemAddress & 0xFF) | (value << 8);
|
||||
}
|
||||
else {
|
||||
emul.ppu.registers.PPUMemAddress =
|
||||
(emul.ppu.registers.PPUMemAddress & 0xFF00) | (value & 0xFF);
|
||||
}
|
||||
#if 0
|
||||
fprintf (stdout, "Ecriture 2006: %x, value: %x\n",
|
||||
emul.ppu.registers.PPUMemAddress, value);
|
||||
#endif
|
||||
/* Change it. */
|
||||
emul.ppu.registers.accessPPUMem = !emul.ppu.registers.accessPPUMem;
|
||||
DEBUG2 (("Address of the address PPU addr pointer: 0x%x, value: %x\n",
|
||||
emul.ppu.registers.PPUMemAddress, value));
|
||||
break;
|
||||
case 0x2007:
|
||||
/* PPU memory Data */
|
||||
DEBUG0 (("2007 - save [0x%x]=0x%x\n", emul.ppu.registers.PPUMemAddress,
|
||||
value));
|
||||
PPU_Write_Vram (emul.ppu.registers.PPUMemAddress, value);
|
||||
if (emul.ppu.registers.ControlRegister1 & CONTROL_INC) {
|
||||
emul.ppu.registers.PPUMemAddress += 32;
|
||||
}
|
||||
else {
|
||||
emul.ppu.registers.PPUMemAddress++;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
DEBUG0 (("Impossible to read at this address (0x%x)\n"
|
||||
"we stop the Emulator.\n ", addr));
|
||||
exit (0);
|
||||
}
|
||||
}
|
||||
|
||||
/*******************************************************
|
||||
* This function Draw a pixel on the Screen.
|
||||
*/
|
||||
void DrawPixel (SDL_Surface *screen, int x, int y, Uint8 color)
|
||||
{
|
||||
Uint8 *bufp;
|
||||
|
||||
/* Get the address of the Pixel. */
|
||||
bufp = (Uint8 *)screen->pixels + y*screen->pitch + x;
|
||||
/* Write the Color. */
|
||||
*bufp = color;
|
||||
}
|
||||
|
||||
/*******************************************************
|
||||
* This function Draw a Tile on the Screen.
|
||||
*/
|
||||
void DrawTile (uint8_t x, uint8_t y, uint16_t StartTile, uint8_t startX,
|
||||
uint16_t BGpatternAddr)
|
||||
{
|
||||
uint8_t *pData1, *pData2;
|
||||
uint8_t color, attr;
|
||||
uint16_t numTile;
|
||||
int i, j, px, py;
|
||||
|
||||
pData1 = &emul.ppu.vram [StartTile];
|
||||
pData2 = &emul.ppu.vram [StartTile + 8];
|
||||
DEBUG2 (("DrawTile x: %d, y:%d\n", x, y));
|
||||
|
||||
/* Get the Num Tile. */
|
||||
numTile = (y * NES_TILES_WIDTH) + x;
|
||||
|
||||
/* Get the Attribute for this Tile. */
|
||||
attr = (emul.ppu.vram[BGpatternAddr + attr_tiles[numTile].square]
|
||||
>> attr_tiles[numTile].shift) & 0x03;
|
||||
|
||||
/* For all the line of the tile. */
|
||||
for (j = 0; j < 8; j++) {
|
||||
DEBUG2 (("(%d)Depart: %d et %d\n", StartTile, *pData1, *pData2));
|
||||
/* For all the Column of the tile. */
|
||||
for (i = startX; i < 8; i++) {
|
||||
/* bit1: Fill Hight part.*/
|
||||
color = ((*pData2 >> (7-i)) & 0x1) << 1;
|
||||
/* bit0: Fill Low part.*/
|
||||
color |= ((*pData1 >> (7-i)) & 0x1);
|
||||
if (color != 0) {
|
||||
/* bit2-3: */
|
||||
color |= (attr << 2);
|
||||
color = PPU_Read_Vram (PICTURE_PALETTE + color);
|
||||
|
||||
px = (x * 8) + i;
|
||||
py = (y * 8) + j;
|
||||
|
||||
DEBUG2 (("color makup: hight: %d, low: %d = color: %d (x:%d,y:%d)\n",
|
||||
((*pData2 >> (7-i)) & 0x1), ((*pData1 >> (7-i)) & 0x1), color,
|
||||
px, py));
|
||||
|
||||
DrawPixel (emul.ppu.screen, px, py, color);
|
||||
}
|
||||
}
|
||||
pData1++;
|
||||
pData2++;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************
|
||||
* This function redraw the Screen.
|
||||
*/
|
||||
void PPU_RedrawNesScreen ()
|
||||
{
|
||||
int x, y;
|
||||
uint16_t pTileArea, screenPatAddr, BGpatternAddr;
|
||||
uint8_t value;
|
||||
DEBUG2 (("%s\n", __FUNCTION__));
|
||||
|
||||
/* Clear the Screen. */
|
||||
SDL_FillRect (emul.ppu.screen, 0, emul.ppu.vram[PICTURE_PALETTE]);
|
||||
|
||||
//emul.ppu.registers.StatusRegister = 0;
|
||||
if ((emul.ppu.registers.ControlRegister1 & CONTROL_TAB_ADDR) == 0) {
|
||||
pTileArea = PAGE0;
|
||||
BGpatternAddr = ATTRIBUTE_TABLE_0;
|
||||
}
|
||||
else if ((emul.ppu.registers.ControlRegister1 & CONTROL_TAB_ADDR) == 1) {
|
||||
pTileArea = PAGE1;
|
||||
BGpatternAddr = ATTRIBUTE_TABLE_1;
|
||||
}
|
||||
else if ((emul.ppu.registers.ControlRegister1 & CONTROL_TAB_ADDR) == 2) {
|
||||
pTileArea = PAGE2;
|
||||
BGpatternAddr = ATTRIBUTE_TABLE_2;
|
||||
}
|
||||
else {
|
||||
pTileArea = PAGE3;
|
||||
BGpatternAddr = ATTRIBUTE_TABLE_3;
|
||||
}
|
||||
|
||||
if (emul.ppu.registers.ControlRegister1 & CONTROL_SCREEN_PATT_ADDR) {
|
||||
screenPatAddr = 0x1000;
|
||||
}
|
||||
else {
|
||||
screenPatAddr = 0;
|
||||
}
|
||||
|
||||
/* Boucle to draw. */
|
||||
for (y = 0; y < NES_TILES_HEIGHT; y++) {
|
||||
for (x = 0; x < NES_TILES_WIDTH; x++) {
|
||||
value = emul.ppu.vram [pTileArea++];
|
||||
DEBUG2 (("On veut le tile: %x\n", value));
|
||||
DrawTile (x, y, ((value * 16) + screenPatAddr), 0, BGpatternAddr);
|
||||
}
|
||||
}
|
||||
|
||||
/* Then Update the Screen */
|
||||
SDL_UpdateRect(emul.ppu.screen, 0, 0, NES_WIDTH, NES_HEIGHT);
|
||||
|
||||
//emul.ppu.registers.StatusRegister = 0x80;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************
|
||||
* Change One Bit of the PPU status.
|
||||
*/
|
||||
void bitsetPPUStatus (uint8_t flag, uint8_t val)
|
||||
{
|
||||
if (val == 0) {
|
||||
emul.ppu.registers.StatusRegister &= ~(1 << flag);
|
||||
}
|
||||
else {
|
||||
emul.ppu.registers.StatusRegister |= (1 << flag);
|
||||
}
|
||||
}
|
||||
|
||||
/*******************************************************
|
||||
* This function Show the Tiles in Rom.
|
||||
*/
|
||||
void PPU_ShowTiles (int page)
|
||||
{
|
||||
int x, y;
|
||||
uint16_t screenPatAddr;
|
||||
uint8_t value;
|
||||
uint16_t BGpatternAddr;
|
||||
|
||||
fprintf (stdout, "Print the Tile Page: %d\n", page);
|
||||
|
||||
/* Set the Start address of the Tiles area. */
|
||||
if (page == 1) {
|
||||
screenPatAddr = 0x1000;
|
||||
}
|
||||
else {
|
||||
screenPatAddr = 0;
|
||||
}
|
||||
|
||||
value = 0;
|
||||
|
||||
/* Boucle to draw. */
|
||||
for (y = 0; y < NES_TILES_HEIGHT/2; y++) {
|
||||
for (x = 0; x < (NES_TILES_WIDTH/2) + 1; x++) {
|
||||
|
||||
DEBUG2 (("On veut le tile: %x (%d,%d)\n", value, x, y));
|
||||
DrawTile (x, y, ((value * 16) + screenPatAddr), 0, BGpatternAddr);
|
||||
value++;
|
||||
}
|
||||
}
|
||||
|
||||
/* Then Update the Screen*/
|
||||
SDL_UpdateRect (emul.ppu.screen, 0, 0, NES_WIDTH, NES_HEIGHT);
|
||||
}
|
||||
|
||||
/*******************************************************
|
||||
* This function Clear the Screen.
|
||||
*/
|
||||
void PPU_ClearScreen ()
|
||||
{
|
||||
SDL_FillRect (emul.ppu.screen, 0, emul.ppu.vram[PICTURE_PALETTE]);
|
||||
|
||||
/* Then Update the Screen*/
|
||||
SDL_UpdateRect (emul.ppu.screen, 0, 0, NES_WIDTH, NES_HEIGHT);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************
|
||||
* This function Clear the Screen.
|
||||
*/
|
||||
void PPU_DMA_SpriteCopy (uint8_t value)
|
||||
{
|
||||
int i;
|
||||
uint16_t cpt;
|
||||
uint16_t addr;
|
||||
|
||||
cpt = emul.ppu.registers.SpriteMemAddress;
|
||||
addr = 0x100 * value;
|
||||
|
||||
for (i=0; i < 0x100; i++) {
|
||||
if (cpt == SPRITE_RAM_SIZE) {
|
||||
cpt = 0;
|
||||
}
|
||||
/* Copy */
|
||||
emul.ppu.sprite_ram [cpt++] = memory_Read (addr++);
|
||||
}
|
||||
}
|
||||
130
src/emulator/ppu.h
Normal file
130
src/emulator/ppu.h
Normal file
@@ -0,0 +1,130 @@
|
||||
/*----------------------------------------------------------------------------
|
||||
* ppu.h. ppu structure.
|
||||
*
|
||||
*----------------------------------------------------------------------------
|
||||
* nes: Nintendo Entertainment System Emulator.
|
||||
*
|
||||
* Created by Jean-Baptiste Nadal on 12/02/08.
|
||||
* Copyright 2008. All rights reserved.
|
||||
*----------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#ifndef __PPU_H
|
||||
#define __PPU_H
|
||||
|
||||
#define PAL_NMI_TICK 20
|
||||
|
||||
#define VRAM_SIZE 16384
|
||||
|
||||
#define SPRITE_RAM_SIZE 256
|
||||
|
||||
/*
|
||||
* Memory Map of the VRAM:
|
||||
* ======================
|
||||
*
|
||||
* +----------+-------+-------+----------------------+
|
||||
* | Address | End | Size | Description |
|
||||
* +----------+-------+-------+----------------------+
|
||||
* | $0000 | $0FFF | $1000 | Pattern Table #0 |
|
||||
* +----------+-------+-------+----------------------+
|
||||
* | $1000 | $1FFF | $1000 | Pattern Table #1 |
|
||||
* +----------+-------+-------+----------------------+
|
||||
* | $2000 | $23BF | $3C0 | Name Table #0 |
|
||||
* +----------+-------+-------+----------------------+
|
||||
* | $23C0 | $23FF | $40 | Attribute Table #0 |
|
||||
* +----------+-------+-------+----------------------+
|
||||
* | $2400 | $27BF | $3C0 | Name Table #1 |
|
||||
* +----------+-------+-------+----------------------+
|
||||
* | $27C0 | $27FF | $40 | Attribute Table #1 |
|
||||
* +----------+-------+-------+----------------------+
|
||||
* | $2800 | $2BBF | $3C0 | Name Table #2 |
|
||||
* +----------+-------+-------+----------------------+
|
||||
* | $2BC0 | $2BFF | $40 | Attribute Table #2 |
|
||||
* +----------+-------+-------+----------------------+
|
||||
* | $2C00 | $2FBF | $3C0 | Name Table #2 |
|
||||
* +----------+-------+-------+----------------------+
|
||||
* | $2FC0 | $2FFF | $40 | Attribute Table #2 |
|
||||
* +----------+-------+-------+----------------------+
|
||||
* | $3000 | $3EFF | $EFF | Not Used |
|
||||
* +----------+-------+-------+----------------------+
|
||||
* | $3F00 | $3F0F | $10 | Picture Palette |
|
||||
* +----------+-------+-------+----------------------+
|
||||
* | $3F10 | $3F1F | $10 | Sprite Palette |
|
||||
* +----------+-------+-------+----------------------+
|
||||
* | $3F20 | $3FFF | $E0 | Mirror of Palettes |
|
||||
* +----------+-------+-------+----------------------+
|
||||
*
|
||||
*/
|
||||
|
||||
#define PATTERN_TABLE_0 0x0000
|
||||
#define PATTERN_TABLE_1 0x1000
|
||||
#define NAME_TABLE_0 0x2000
|
||||
#define ATTRIBUTE_TABLE_0 0x23C0
|
||||
#define NAME_TABLE_1 0x2400
|
||||
#define ATTRIBUTE_TABLE_1 0x27C0
|
||||
#define NAME_TABLE_2 0x2800
|
||||
#define ATTRIBUTE_TABLE_2 0x2BC0
|
||||
#define NAME_TABLE_3 0x2C00
|
||||
#define ATTRIBUTE_TABLE_3 0x2FC0
|
||||
#define PICTURE_PALETTE 0x3F00
|
||||
#define SPRITE_PALETTE 0x3F10
|
||||
|
||||
|
||||
#define PAGE0 0x2000
|
||||
#define PAGE1 0x2400
|
||||
#define PAGE2 0x2800
|
||||
#define PAGE3 0x2C00
|
||||
|
||||
#define NES_WIDTH 256
|
||||
#define NES_HEIGHT 240
|
||||
|
||||
#define NES_TILES_WIDTH 32 /* 256 / 8 */
|
||||
#define NES_TILES_HEIGHT 30 /* 240 / 8 */
|
||||
|
||||
#define ACCESS_HIGH 1
|
||||
#define ACCESS_LOW 0
|
||||
|
||||
#define CONTROL_INC 0x04
|
||||
#define CONTROL_BACKGROUND_PATTERN 0x08
|
||||
#define CONTROL_TAB_ADDR 0x03
|
||||
#define CONTROL_SCREEN_PATT_ADDR 0x016
|
||||
|
||||
typedef struct ppu_register_t {
|
||||
/* PPU Control Register 1. */
|
||||
uint8_t ControlRegister1;
|
||||
/* PPU Control Register 2. */
|
||||
uint8_t ControlRegister2;
|
||||
/* PPU Status Register. */
|
||||
uint8_t StatusRegister;
|
||||
/* PPU Screen Scroll Offset. */
|
||||
uint8_t ScreenScrollOffset;
|
||||
/* PPU Tiles Address. */
|
||||
uint16_t PPUMemAddress;
|
||||
uint8_t accessPPUMem;
|
||||
|
||||
/* PPU Sprites Address. */
|
||||
uint8_t SpriteMemAddress;
|
||||
|
||||
}ppu_register_t;
|
||||
|
||||
typedef struct nes_ppu_t {
|
||||
|
||||
ppu_register_t registers;
|
||||
/* Kind of boolean to init or not the PPU. */
|
||||
uint8_t init;
|
||||
/* Timer for the PPU's NMI (Non Maskable Interrupt */
|
||||
SDL_TimerID timerID;
|
||||
/* Main Screen. */
|
||||
SDL_Surface *screen;
|
||||
/* Temporary buffer to transfert PPU data. */
|
||||
uint8_t PPU_transfert;
|
||||
/* Video Ram. */
|
||||
uint8_t vram [VRAM_SIZE];
|
||||
/* Sprite Ram. */
|
||||
uint8_t sprite_ram [SPRITE_RAM_SIZE];
|
||||
|
||||
uint8_t spriteHit;
|
||||
}nes_ppu_t;
|
||||
|
||||
|
||||
#endif
|
||||
212
src/emulator/ppu_tiles.h
Normal file
212
src/emulator/ppu_tiles.h
Normal file
@@ -0,0 +1,212 @@
|
||||
/*----------------------------------------------------------------------------
|
||||
* NES PPU: ppu_tiles.h. This file contain array for the link between the
|
||||
* tile Number and the Square position.
|
||||
*
|
||||
*----------------------------------------------------------------------------
|
||||
* nes: Nintendo Entertainment System Emulator.
|
||||
*
|
||||
* Created by Jean-Baptiste Nadal on 13/03/08.
|
||||
* Copyright 2008. All rights reserved.
|
||||
*----------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#ifndef __PPU_TILES_H
|
||||
#define __PPU_TILES_H
|
||||
|
||||
typedef struct attr_elem_t {
|
||||
uint8_t square;
|
||||
uint8_t shift;
|
||||
}attr_elem_t;
|
||||
|
||||
#define E1 00
|
||||
#define E2 02
|
||||
#define E3 04
|
||||
#define E4 06
|
||||
|
||||
attr_elem_t attr_tiles[] = {
|
||||
/* Line 1. */
|
||||
{ 0,E1}, { 0,E1}, { 0,E2}, { 0,E2}, { 1,E1}, { 1,E1}, { 1,E2}, { 1,E2},
|
||||
{ 2,E1}, { 2,E1}, { 2,E2}, { 2,E2}, { 3,E1}, { 3,E1}, { 3,E2}, { 3,E2},
|
||||
{ 4,E1}, { 4,E1}, { 4,E2}, { 4,E2}, { 5,E1}, { 5,E1}, { 5,E2}, { 5,E2},
|
||||
{ 6,E1}, { 6,E1}, { 6,E2}, { 6,E2}, { 7,E1}, { 7,E1}, { 7,E2}, { 7,E2},
|
||||
|
||||
/* Line 2. */
|
||||
{ 0,E1}, { 0,E1}, { 0,E2}, { 0,E2}, { 1,E1}, { 1,E1}, { 1,E2}, { 1,E2},
|
||||
{ 2,E1}, { 2,E1}, { 2,E2}, { 2,E2}, { 3,E1}, { 3,E1}, { 3,E2}, { 3,E2},
|
||||
{ 4,E1}, { 4,E1}, { 4,E2}, { 4,E2}, { 5,E1}, { 5,E1}, { 5,E2}, { 5,E2},
|
||||
{ 6,E1}, { 6,E1}, { 6,E2}, { 6,E2}, { 7,E1}, { 7,E1}, { 7,E2}, { 7,E2},
|
||||
|
||||
/* Line 3. */
|
||||
{ 0,E3}, { 0,E3}, { 0,E4}, { 0,E4}, { 1,E3}, { 1,E3}, { 1,E4}, { 1,E4},
|
||||
{ 2,E3}, { 2,E3}, { 2,E4}, { 2,E4}, { 3,E3}, { 3,E3}, { 3,E4}, { 3,E4},
|
||||
{ 4,E3}, { 4,E3}, { 4,E4}, { 4,E4}, { 5,E3}, { 5,E3}, { 5,E4}, { 5,E4},
|
||||
{ 6,E3}, { 6,E3}, { 6,E4}, { 6,E4}, { 7,E3}, { 7,E3}, { 7,E4}, { 7,E4},
|
||||
|
||||
/* Line 4. */
|
||||
{ 0,E3}, { 0,E3}, { 0,E4}, { 0,E4}, { 1,E3}, { 1,E3}, { 1,E4}, { 1,E4},
|
||||
{ 2,E3}, { 2,E3}, { 2,E4}, { 2,E4}, { 3,E3}, { 3,E3}, { 3,E4}, { 3,E4},
|
||||
{ 4,E3}, { 4,E3}, { 4,E4}, { 4,E4}, { 5,E3}, { 5,E3}, { 5,E4}, { 5,E4},
|
||||
{ 6,E3}, { 6,E3}, { 6,E4}, { 6,E4}, { 7,E3}, { 7,E3}, { 7,E4}, { 7,E4},
|
||||
|
||||
/* Line 5. */
|
||||
{ 8,E1}, { 8,E1}, { 8,E2}, { 8,E2}, { 9,E1}, { 9,E1}, { 9,E2}, { 9,E2},
|
||||
{10,E1}, {10,E1}, {10,E2}, {10,E2}, {11,E1}, {11,E1}, {11,E2}, {11,E2},
|
||||
{12,E1}, {12,E1}, {12,E2}, {12,E2}, {13,E1}, {13,E1}, {13,E2}, {13,E2},
|
||||
{14,E1}, {14,E1}, {14,E2}, {14,E2}, {15,E1}, {15,E1}, {15,E2}, {15,E2},
|
||||
|
||||
/* Line 6. */
|
||||
{ 8,E1}, { 8,E1}, { 8,E2}, { 8,E2}, { 9,E1}, { 9,E1}, { 9,E2}, { 9,E2},
|
||||
{10,E1}, {10,E1}, {10,E2}, {10,E2}, {11,E1}, {11,E1}, {11,E2}, {11,E2},
|
||||
{12,E1}, {12,E1}, {12,E2}, {12,E2}, {13,E1}, {13,E1}, {13,E2}, {13,E2},
|
||||
{14,E1}, {14,E1}, {14,E2}, {14,E2}, {15,E1}, {15,E1}, {15,E2}, {15,E2},
|
||||
|
||||
/* Line 7. */
|
||||
{ 8,E3}, { 8,E3}, { 8,E4}, { 8,E4}, { 9,E3}, { 9,E3}, { 9,E4}, { 9,E4},
|
||||
{10,E3}, {10,E3}, {10,E4}, {10,E4}, {11,E3}, {11,E3}, {11,E4}, {11,E4},
|
||||
{12,E3}, {12,E3}, {12,E4}, {12,E4}, {13,E3}, {13,E3}, {13,E4}, {13,E4},
|
||||
{14,E3}, {14,E3}, {14,E4}, {14,E4}, {15,E3}, {15,E3}, {15,E4}, {15,E4},
|
||||
|
||||
/* Line 8. */
|
||||
{ 8,E3}, { 8,E3}, { 8,E4}, { 8,E4}, { 9,E3}, { 9,E3}, { 9,E4}, { 9,E4},
|
||||
{10,E3}, {10,E3}, {10,E4}, {10,E4}, {11,E3}, {11,E3}, {11,E4}, {11,E4},
|
||||
{12,E3}, {12,E3}, {12,E4}, {12,E4}, {13,E3}, {13,E3}, {13,E4}, {13,E4},
|
||||
{14,E3}, {14,E3}, {14,E4}, {14,E4}, {15,E3}, {15,E3}, {15,E4}, {15,E4},
|
||||
|
||||
/* Line 9. */
|
||||
{16,E1}, {16,E1}, {16,E2}, {16,E2}, {17,E1}, {17,E1}, {17,E2}, {17,E2},
|
||||
{18,E1}, {18,E1}, {18,E2}, {18,E2}, {19,E1}, {19,E1}, {19,E2}, {19,E2},
|
||||
{20,E1}, {20,E1}, {20,E2}, {20,E2}, {21,E1}, {21,E1}, {21,E2}, {21,E2},
|
||||
{22,E1}, {22,E1}, {22,E2}, {22,E2}, {23,E1}, {23,E1}, {23,E2}, {23,E2},
|
||||
|
||||
/* Line 10. */
|
||||
{16,E1}, {16,E1}, {16,E2}, {16,E2}, {17,E1}, {17,E1}, {17,E2}, {17,E2},
|
||||
{18,E1}, {18,E1}, {18,E2}, {18,E2}, {19,E1}, {19,E1}, {19,E2}, {19,E2},
|
||||
{20,E1}, {20,E1}, {20,E2}, {20,E2}, {21,E1}, {21,E1}, {21,E2}, {21,E2},
|
||||
{22,E1}, {22,E1}, {22,E2}, {22,E2}, {23,E1}, {23,E1}, {23,E2}, {23,E2},
|
||||
|
||||
/* Line 11. */
|
||||
{16,E3}, {16,E3}, {16,E4}, {16,E4}, {17,E3}, {17,E3}, {17,E4}, {17,E4},
|
||||
{18,E3}, {18,E3}, {18,E4}, {18,E4}, {19,E3}, {19,E3}, {19,E4}, {19,E4},
|
||||
{20,E3}, {20,E3}, {20,E4}, {20,E4}, {21,E3}, {21,E3}, {21,E4}, {21,E4},
|
||||
{22,E3}, {22,E3}, {22,E4}, {22,E4}, {23,E3}, {23,E3}, {23,E4}, {23,E4},
|
||||
|
||||
/* Line 12. */
|
||||
{16,E3}, {16,E3}, {16,E4}, {16,E4}, {17,E3}, {17,E3}, {17,E4}, {17,E4},
|
||||
{18,E3}, {18,E3}, {18,E4}, {18,E4}, {19,E3}, {19,E3}, {19,E4}, {19,E4},
|
||||
{20,E3}, {20,E3}, {20,E4}, {20,E4}, {21,E3}, {21,E3}, {21,E4}, {21,E4},
|
||||
{22,E3}, {22,E3}, {22,E4}, {22,E4}, {23,E3}, {23,E3}, {23,E4}, {23,E4},
|
||||
|
||||
/* Line 13. */
|
||||
{24,E1}, {24,E1}, {24,E2}, {24,E2}, {25,E1}, {25,E1}, {25,E2}, {25,E2},
|
||||
{26,E1}, {26,E1}, {26,E2}, {26,E2}, {27,E1}, {27,E1}, {27,E2}, {27,E2},
|
||||
{28,E1}, {28,E1}, {28,E2}, {28,E2}, {29,E1}, {29,E1}, {29,E2}, {29,E2},
|
||||
{30,E1}, {30,E1}, {30,E2}, {30,E2}, {31,E1}, {31,E1}, {31,E2}, {31,E2},
|
||||
|
||||
/* Line 14. */
|
||||
{24,E1}, {24,E1}, {24,E2}, {24,E2}, {25,E1}, {25,E1}, {25,E2}, {25,E2},
|
||||
{26,E1}, {26,E1}, {26,E2}, {26,E2}, {27,E1}, {27,E1}, {27,E2}, {27,E2},
|
||||
{28,E1}, {28,E1}, {28,E2}, {28,E2}, {29,E1}, {29,E1}, {29,E2}, {29,E2},
|
||||
{30,E1}, {30,E1}, {30,E2}, {30,E2}, {31,E1}, {31,E1}, {31,E2}, {31,E2},
|
||||
|
||||
/* Line 15. */
|
||||
{24,E3}, {24,E3}, {24,E4}, {24,E4}, {25,E3}, {25,E3}, {25,E4}, {25,E4},
|
||||
{26,E3}, {26,E3}, {26,E4}, {26,E4}, {27,E3}, {27,E3}, {27,E4}, {27,E4},
|
||||
{28,E3}, {28,E3}, {28,E4}, {28,E4}, {29,E3}, {29,E3}, {29,E4}, {29,E4},
|
||||
{30,E3}, {30,E3}, {30,E4}, {30,E4}, {31,E3}, {31,E3}, {31,E4}, {31,E4},
|
||||
|
||||
/* Line 16. */
|
||||
{24,E3}, {24,E3}, {24,E4}, {24,E4}, {25,E3}, {25,E3}, {25,E4}, {25,E4},
|
||||
{26,E3}, {26,E3}, {26,E4}, {26,E4}, {27,E3}, {27,E3}, {27,E4}, {27,E4},
|
||||
{28,E3}, {28,E3}, {28,E4}, {28,E4}, {29,E3}, {29,E3}, {29,E4}, {29,E4},
|
||||
{30,E3}, {30,E3}, {30,E4}, {30,E4}, {31,E3}, {31,E3}, {31,E4}, {31,E4},
|
||||
|
||||
/* Line 17. */
|
||||
{32,E1}, {32,E1}, {32,E2}, {32,E2}, {33,E1}, {33,E1}, {33,E2}, {33,E2},
|
||||
{34,E1}, {34,E1}, {34,E2}, {34,E2}, {35,E1}, {35,E1}, {35,E2}, {35,E2},
|
||||
{36,E1}, {36,E1}, {36,E2}, {36,E2}, {37,E1}, {37,E1}, {37,E2}, {37,E2},
|
||||
{38,E1}, {38,E1}, {38,E2}, {38,E2}, {39,E1}, {39,E1}, {39,E2}, {39,E2},
|
||||
|
||||
/* Line 18. */
|
||||
{32,E1}, {32,E1}, {32,E2}, {32,E2}, {33,E1}, {33,E1}, {33,E2}, {33,E2},
|
||||
{34,E1}, {34,E1}, {34,E2}, {34,E2}, {35,E1}, {35,E1}, {35,E2}, {35,E2},
|
||||
{36,E1}, {36,E1}, {36,E2}, {36,E2}, {37,E1}, {37,E1}, {37,E2}, {37,E2},
|
||||
{38,E1}, {38,E1}, {38,E2}, {38,E2}, {39,E1}, {39,E1}, {39,E2}, {39,E2},
|
||||
|
||||
/* Line 19. */
|
||||
{32,E3}, {32,E3}, {32,E4}, {32,E4}, {33,E3}, {33,E3}, {33,E4}, {33,E4},
|
||||
{34,E3}, {34,E3}, {34,E4}, {34,E4}, {35,E3}, {35,E3}, {35,E4}, {35,E4},
|
||||
{36,E3}, {36,E3}, {36,E4}, {36,E4}, {37,E3}, {37,E3}, {37,E4}, {37,E4},
|
||||
{38,E3}, {38,E3}, {38,E4}, {38,E4}, {39,E3}, {39,E3}, {39,E4}, {39,E4},
|
||||
|
||||
/* Line 20. */
|
||||
{32,E3}, {32,E3}, {32,E4}, {32,E4}, {33,E3}, {33,E3}, {33,E4}, {33,E4},
|
||||
{34,E3}, {34,E3}, {34,E4}, {34,E4}, {35,E3}, {35,E3}, {35,E4}, {35,E4},
|
||||
{36,E3}, {36,E3}, {36,E4}, {36,E4}, {37,E3}, {37,E3}, {37,E4}, {37,E4},
|
||||
{38,E3}, {38,E3}, {38,E4}, {38,E4}, {39,E3}, {39,E3}, {39,E4}, {39,E4},
|
||||
|
||||
/* Line 21. */
|
||||
{40,E1}, {40,E1}, {40,E2}, {40,E2}, {41,E1}, {41,E1}, {41,E2}, {41,E2},
|
||||
{42,E1}, {42,E1}, {42,E2}, {42,E2}, {43,E1}, {43,E1}, {43,E2}, {43,E2},
|
||||
{44,E1}, {44,E1}, {44,E2}, {44,E2}, {45,E1}, {45,E1}, {45,E2}, {45,E2},
|
||||
{46,E1}, {46,E1}, {46,E2}, {46,E2}, {47,E1}, {47,E1}, {47,E2}, {47,E2},
|
||||
|
||||
/* Line 22. */
|
||||
{40,E1}, {40,E1}, {40,E2}, {40,E2}, {41,E1}, {41,E1}, {41,E2}, {41,E2},
|
||||
{42,E1}, {42,E1}, {42,E2}, {42,E2}, {43,E1}, {43,E1}, {43,E2}, {43,E2},
|
||||
{44,E1}, {44,E1}, {44,E2}, {44,E2}, {45,E1}, {45,E1}, {45,E2}, {45,E2},
|
||||
{46,E1}, {46,E1}, {46,E2}, {46,E2}, {47,E1}, {47,E1}, {47,E2}, {47,E2},
|
||||
|
||||
/* Line 23. */
|
||||
{40,E3}, {40,E3}, {40,E4}, {40,E4}, {41,E3}, {41,E3}, {41,E4}, {41,E4},
|
||||
{42,E3}, {42,E3}, {42,E4}, {42,E4}, {43,E3}, {43,E3}, {43,E4}, {43,E4},
|
||||
{44,E3}, {44,E3}, {44,E4}, {44,E4}, {45,E3}, {45,E3}, {45,E4}, {45,E4},
|
||||
{46,E3}, {46,E3}, {46,E4}, {46,E4}, {47,E3}, {47,E3}, {47,E4}, {47,E4},
|
||||
|
||||
/* Line 24. */
|
||||
{40,E3}, {40,E3}, {40,E4}, {40,E4}, {41,E3}, {41,E3}, {41,E4}, {41,E4},
|
||||
{42,E3}, {42,E3}, {42,E4}, {42,E4}, {43,E3}, {43,E3}, {43,E4}, {43,E4},
|
||||
{44,E3}, {44,E3}, {44,E4}, {44,E4}, {45,E3}, {45,E3}, {45,E4}, {45,E4},
|
||||
{46,E3}, {46,E3}, {46,E4}, {46,E4}, {47,E3}, {47,E3}, {47,E4}, {47,E4},
|
||||
|
||||
/* Line 25. */
|
||||
{48,E1}, {48,E1}, {48,E2}, {48,E2}, {49,E1}, {49,E1}, {49,E2}, {49,E2},
|
||||
{50,E1}, {50,E1}, {50,E2}, {50,E2}, {51,E1}, {51,E1}, {51,E2}, {51,E2},
|
||||
{52,E1}, {52,E1}, {52,E2}, {52,E2}, {53,E1}, {53,E1}, {53,E2}, {53,E2},
|
||||
{54,E1}, {54,E1}, {54,E2}, {54,E2}, {55,E1}, {55,E1}, {55,E2}, {55,E2},
|
||||
|
||||
/* Line 26. */
|
||||
{48,E1}, {48,E1}, {48,E2}, {48,E2}, {49,E1}, {49,E1}, {49,E2}, {49,E2},
|
||||
{50,E1}, {50,E1}, {50,E2}, {50,E2}, {51,E1}, {51,E1}, {51,E2}, {51,E2},
|
||||
{52,E1}, {52,E1}, {52,E2}, {52,E2}, {53,E1}, {53,E1}, {53,E2}, {53,E2},
|
||||
{54,E1}, {54,E1}, {54,E2}, {54,E2}, {55,E1}, {55,E1}, {55,E2}, {55,E2},
|
||||
|
||||
/* Line 27. */
|
||||
{48,E3}, {48,E3}, {48,E4}, {48,E4}, {49,E3}, {49,E3}, {49,E4}, {49,E4},
|
||||
{50,E3}, {50,E3}, {50,E4}, {50,E4}, {51,E3}, {51,E3}, {51,E4}, {51,E4},
|
||||
{52,E3}, {52,E3}, {52,E4}, {52,E4}, {53,E3}, {53,E3}, {53,E4}, {53,E4},
|
||||
{54,E3}, {54,E3}, {54,E4}, {54,E4}, {55,E3}, {55,E3}, {55,E4}, {55,E4},
|
||||
|
||||
/* Line 28. */
|
||||
{48,E3}, {48,E3}, {48,E4}, {48,E4}, {49,E3}, {49,E3}, {49,E4}, {49,E4},
|
||||
{50,E3}, {50,E3}, {50,E4}, {50,E4}, {51,E3}, {51,E3}, {51,E4}, {51,E4},
|
||||
{52,E3}, {52,E3}, {52,E4}, {52,E4}, {53,E3}, {53,E3}, {53,E4}, {53,E4},
|
||||
{54,E3}, {54,E3}, {54,E4}, {54,E4}, {55,E3}, {55,E3}, {55,E4}, {55,E4},
|
||||
|
||||
/* Line 29. */
|
||||
{56,E1}, {56,E1}, {56,E2}, {56,E2}, {57,E1}, {57,E1}, {57,E2}, {57,E2},
|
||||
{58,E1}, {58,E1}, {58,E2}, {58,E2}, {59,E1}, {59,E1}, {59,E2}, {59,E2},
|
||||
{60,E1}, {60,E1}, {60,E2}, {60,E2}, {61,E1}, {61,E1}, {61,E2}, {61,E2},
|
||||
{62,E1}, {62,E1}, {62,E2}, {62,E2}, {63,E1}, {63,E1}, {63,E2}, {63,E2},
|
||||
|
||||
/* Line 30. */
|
||||
{56,E1}, {56,E1}, {56,E2}, {56,E2}, {57,E1}, {57,E1}, {57,E2}, {57,E2},
|
||||
{58,E1}, {58,E1}, {58,E2}, {58,E2}, {59,E1}, {59,E1}, {59,E2}, {59,E2},
|
||||
{60,E1}, {60,E1}, {60,E2}, {60,E2}, {61,E1}, {61,E1}, {61,E2}, {61,E2},
|
||||
{62,E1}, {62,E1}, {62,E2}, {62,E2}, {63,E1}, {63,E1}, {63,E2}, {63,E2},
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
120
src/main.c
Normal file
120
src/main.c
Normal file
@@ -0,0 +1,120 @@
|
||||
/*----------------------------------------------------------------------------
|
||||
* main.c: Main file for the JB's nes Emulator.
|
||||
*
|
||||
*----------------------------------------------------------------------------
|
||||
* nes: Nintendo Entertainment System Emulator.
|
||||
*
|
||||
* Created by Jean-Baptiste Nadal on 12/02/08.
|
||||
* Copyright 2008. All rights reserved.
|
||||
*----------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
/* SDL's includes */
|
||||
#include <SDL/SDL.h>
|
||||
|
||||
/* Systems */
|
||||
#include <getopt.h>
|
||||
|
||||
/*nes includes */
|
||||
#include "cpu6502.h"
|
||||
#include "memory.h"
|
||||
#include "ppu.h"
|
||||
#include "apu.h"
|
||||
#include "paddle.h"
|
||||
#include "debugger.h"
|
||||
#include "emulator.h"
|
||||
#include "main.h"
|
||||
|
||||
#include "debug.h"
|
||||
|
||||
/* long option structure */
|
||||
static struct option long_options[] = {
|
||||
{"help", 0, NULL, 'h'},
|
||||
{"input", 1, NULL, 'i'},
|
||||
{0, 0, 0, 0}
|
||||
};
|
||||
|
||||
|
||||
/***************************************************
|
||||
*
|
||||
* Display usage of the program.
|
||||
*
|
||||
*
|
||||
*/
|
||||
void display_usage(void)
|
||||
{
|
||||
fprintf(stderr, "Usage: nes [options] ...\n\n"
|
||||
"Options:\n"
|
||||
"--------\n");
|
||||
fprintf(stderr, "\n -h, --help ");
|
||||
fprintf(stderr, "Display the help message.");
|
||||
fprintf(stderr, "\n -i, --input ");
|
||||
fprintf(stderr, "Give the file name of the ROM file.");
|
||||
}
|
||||
|
||||
|
||||
/***************************************************
|
||||
*
|
||||
* Main Function.
|
||||
*
|
||||
*
|
||||
*/
|
||||
int main (int argc, char **argv)
|
||||
{
|
||||
int result = 0;
|
||||
int c;
|
||||
char *romName = NULL;
|
||||
int debug = 0;
|
||||
|
||||
/* Parse the command line. */
|
||||
while ((c = getopt_long (argc, argv, "dhi:", long_options, NULL)) != -1) {
|
||||
switch (c) {
|
||||
case 'd':
|
||||
debug = 1;
|
||||
break;
|
||||
case 'h':
|
||||
display_usage();
|
||||
break;
|
||||
case 'i':
|
||||
romName = optarg;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (romName == NULL) {
|
||||
fprintf (stderr, "You need to specify an Rom file.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Init the Emulator. */
|
||||
result = emulator_init ();
|
||||
if (result != 0) {
|
||||
fprintf (stderr, "Impossible to init the Emulator.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Load the rom. */
|
||||
result = emulator_load (romName);
|
||||
if (result != 0) {
|
||||
fprintf (stderr, "Impossible to load the Rom.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Init the Debugger. */
|
||||
if (debug == 1) {
|
||||
result = debugger_init ();
|
||||
if (result != 0) {
|
||||
fprintf (stderr, "Impossible to init the Emulator.\n");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Loop on the Commands. */
|
||||
result = emulator_loop (debug);
|
||||
if (result != 0) {
|
||||
DEBUG0 (("Impossible to Loop on the ROM commands.(%d)\n", result));
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
66
src/main.h
Normal file
66
src/main.h
Normal file
@@ -0,0 +1,66 @@
|
||||
/*----------------------------------------------------------------------------
|
||||
* main.h: Main file for the JB's nes Emulator.
|
||||
*
|
||||
*----------------------------------------------------------------------------
|
||||
* nes: Nintendo Entertainment System Emulator.
|
||||
*
|
||||
* Created by Jean-Baptiste Nadal on 12/02/08.
|
||||
* Copyright 2008. All rights reserved.
|
||||
*----------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#ifndef _MAIN_H
|
||||
#define _MAIN_H
|
||||
|
||||
|
||||
#define FALSE 0
|
||||
#define TRUE 1
|
||||
|
||||
/* Functions defined in the emulator.c file. */
|
||||
extern int emulator_init ();
|
||||
extern int emulator_load (char *romName);
|
||||
extern int emulator_reset ();
|
||||
extern int emulator_loop ();
|
||||
|
||||
/* Functions defined in the cpu6502.c file. */
|
||||
extern int cpu6502_init ();
|
||||
extern int cpu6502_execOpCode (nes_cpu6502_t *cpu, uint8_t opcode);
|
||||
extern void printRegs (nes_cpu6502_t *cpu);
|
||||
|
||||
/* Functions defined in the memory.c file. */
|
||||
extern int memory_init ();
|
||||
extern uint8_t memory_fetch (uint16_t addr);
|
||||
extern uint16_t memory_Read16 (uint16_t hight, uint16_t low);
|
||||
extern uint8_t memory_Read (uint16_t addr);
|
||||
extern void memory_Write (uint16_t addr, uint8_t value);
|
||||
extern void memory_WriteIndirectIndexed (uint8_t addr, uint8_t value);
|
||||
extern void DumpMemory (uint16_t StartAddr);
|
||||
|
||||
/* Functions defined in the ppu.c file. */
|
||||
extern int ppu_init ();
|
||||
extern uint8_t memory_Read_PPu (uint16_t addr);
|
||||
extern void memory_Write_PPu (uint16_t addr, uint8_t value);
|
||||
extern void bitsetPPUStatus (uint8_t flag, uint8_t val);
|
||||
extern void PPU_RedrawNesScreen ();
|
||||
extern void PPU_ShowTiles (int page);
|
||||
extern void PPU_ClearScreen ();
|
||||
extern void PPU_DMA_SpriteCopy (uint8_t value);
|
||||
|
||||
/* Functions defined in the apu.c file. */
|
||||
extern int apu_init ();
|
||||
|
||||
/* Functions defined in the debugger.c file. */
|
||||
extern int debugger_init ();
|
||||
extern void debugger_stop ();
|
||||
|
||||
/* Functions defined in the paddle.c file. */
|
||||
extern int paddle_Init (paddle_t *paddle);
|
||||
extern int paddle_Read (paddle_t *paddle);
|
||||
extern void paddle_Write (paddle_t *paddle, uint8_t value);
|
||||
|
||||
/* Global Emulator. */
|
||||
extern nes_emulator_t emul;
|
||||
|
||||
#define DEBUG 1
|
||||
|
||||
#endif
|
||||
1394
tools/corecompare/osx/corecompare.xcodeproj/jbnadal.mode1v3
Normal file
1394
tools/corecompare/osx/corecompare.xcodeproj/jbnadal.mode1v3
Normal file
File diff suppressed because it is too large
Load Diff
149
tools/corecompare/osx/corecompare.xcodeproj/jbnadal.pbxuser
Normal file
149
tools/corecompare/osx/corecompare.xcodeproj/jbnadal.pbxuser
Normal file
@@ -0,0 +1,149 @@
|
||||
// !$*UTF8*$!
|
||||
{
|
||||
08FB7793FE84155DC02AAC07 /* Project object */ = {
|
||||
activeArchitecture = ppc;
|
||||
activeBuildConfigurationName = Release;
|
||||
activeExecutable = EF8E674E0DA226B4000B54B0 /* corecompare */;
|
||||
activeTarget = 8DD76FA90486AB0100D96B5E /* corecompare */;
|
||||
addToTargets = (
|
||||
8DD76FA90486AB0100D96B5E /* corecompare */,
|
||||
);
|
||||
breakpoints = (
|
||||
);
|
||||
codeSenseManager = EF8E67550DA226BA000B54B0 /* Code sense */;
|
||||
executables = (
|
||||
EF8E674E0DA226B4000B54B0 /* corecompare */,
|
||||
);
|
||||
perUserDictionary = {
|
||||
PBXConfiguration.PBXFileTableDataSource3.PBXFileTableDataSource = {
|
||||
PBXFileTableDataSourceColumnSortingDirectionKey = "-1";
|
||||
PBXFileTableDataSourceColumnSortingKey = PBXFileDataSource_Filename_ColumnID;
|
||||
PBXFileTableDataSourceColumnWidthsKey = (
|
||||
20,
|
||||
243,
|
||||
20,
|
||||
48,
|
||||
43,
|
||||
43,
|
||||
20,
|
||||
);
|
||||
PBXFileTableDataSourceColumnsKey = (
|
||||
PBXFileDataSource_FiletypeID,
|
||||
PBXFileDataSource_Filename_ColumnID,
|
||||
PBXFileDataSource_Built_ColumnID,
|
||||
PBXFileDataSource_ObjectSize_ColumnID,
|
||||
PBXFileDataSource_Errors_ColumnID,
|
||||
PBXFileDataSource_Warnings_ColumnID,
|
||||
PBXFileDataSource_Target_ColumnID,
|
||||
);
|
||||
};
|
||||
PBXConfiguration.PBXTargetDataSource.PBXTargetDataSource = {
|
||||
PBXFileTableDataSourceColumnSortingDirectionKey = "-1";
|
||||
PBXFileTableDataSourceColumnSortingKey = PBXFileDataSource_Filename_ColumnID;
|
||||
PBXFileTableDataSourceColumnWidthsKey = (
|
||||
20,
|
||||
203,
|
||||
60,
|
||||
20,
|
||||
48,
|
||||
43,
|
||||
43,
|
||||
);
|
||||
PBXFileTableDataSourceColumnsKey = (
|
||||
PBXFileDataSource_FiletypeID,
|
||||
PBXFileDataSource_Filename_ColumnID,
|
||||
PBXTargetDataSource_PrimaryAttribute,
|
||||
PBXFileDataSource_Built_ColumnID,
|
||||
PBXFileDataSource_ObjectSize_ColumnID,
|
||||
PBXFileDataSource_Errors_ColumnID,
|
||||
PBXFileDataSource_Warnings_ColumnID,
|
||||
);
|
||||
};
|
||||
PBXPerProjectTemplateStateSaveDate = 228834066;
|
||||
PBXWorkspaceStateSaveDate = 228834066;
|
||||
};
|
||||
sourceControlManager = EF8E67540DA226BA000B54B0 /* Source Control */;
|
||||
userBuildSettings = {
|
||||
};
|
||||
};
|
||||
8DD76FA90486AB0100D96B5E /* corecompare */ = {
|
||||
activeExec = 0;
|
||||
executables = (
|
||||
EF8E674E0DA226B4000B54B0 /* corecompare */,
|
||||
);
|
||||
};
|
||||
EF8E674E0DA226B4000B54B0 /* corecompare */ = {
|
||||
isa = PBXExecutable;
|
||||
activeArgIndices = (
|
||||
);
|
||||
argumentStrings = (
|
||||
);
|
||||
autoAttachOnCrash = 1;
|
||||
breakpointsEnabled = 0;
|
||||
configStateDict = {
|
||||
};
|
||||
customDataFormattersEnabled = 1;
|
||||
debuggerPlugin = GDBDebugging;
|
||||
disassemblyDisplayState = 0;
|
||||
dylibVariantSuffix = "";
|
||||
enableDebugStr = 1;
|
||||
environmentEntries = (
|
||||
);
|
||||
executableSystemSymbolLevel = 0;
|
||||
executableUserSymbolLevel = 0;
|
||||
libgmallocEnabled = 0;
|
||||
name = corecompare;
|
||||
sourceDirectories = (
|
||||
);
|
||||
};
|
||||
EF8E67540DA226BA000B54B0 /* Source Control */ = {
|
||||
isa = PBXSourceControlManager;
|
||||
fallbackIsa = XCSourceControlManager;
|
||||
isSCMEnabled = 0;
|
||||
scmConfiguration = {
|
||||
};
|
||||
};
|
||||
EF8E67550DA226BA000B54B0 /* Code sense */ = {
|
||||
isa = PBXCodeSenseManager;
|
||||
indexTemplatePath = "";
|
||||
};
|
||||
EF8E676B0DA22D89000B54B0 /* corecompare.c */ = {
|
||||
uiCtxt = {
|
||||
sepNavIntBoundsRect = "{{0, 0}, {691, 3136}}";
|
||||
sepNavSelRange = "{1892, 0}";
|
||||
sepNavVisRange = "{4014, 1289}";
|
||||
sepNavWindowFrame = "{{530, 104}, {750, 728}}";
|
||||
};
|
||||
};
|
||||
EF8E676E0DA22D92000B54B0 /* corecpu.c */ = {
|
||||
uiCtxt = {
|
||||
sepNavIntBoundsRect = "{{0, 0}, {691, 24976}}";
|
||||
sepNavSelRange = "{812, 0}";
|
||||
sepNavVisRange = "{0, 1590}";
|
||||
sepNavWindowFrame = "{{519, 66}, {750, 728}}";
|
||||
};
|
||||
};
|
||||
EF8E676F0DA22D92000B54B0 /* corecpu.h */ = {
|
||||
uiCtxt = {
|
||||
sepNavIntBoundsRect = "{{0, 0}, {439, 2422}}";
|
||||
sepNavSelRange = "{757, 75}";
|
||||
sepNavVisRange = "{544, 315}";
|
||||
};
|
||||
};
|
||||
EF8E677C0DA231E1000B54B0 /* cpu6502.h */ = {
|
||||
uiCtxt = {
|
||||
sepNavIntBoundsRect = "{{0, 0}, {691, 1330}}";
|
||||
sepNavSelRange = "{1729, 52}";
|
||||
sepNavVisRange = "{1268, 881}";
|
||||
sepNavWindowFrame = "{{421, 93}, {750, 728}}";
|
||||
};
|
||||
};
|
||||
EF8E677D0DA231E1000B54B0 /* cpu6502.c */ = {
|
||||
uiCtxt = {
|
||||
sepNavIntBoundsRect = "{{0, 0}, {691, 42966}}";
|
||||
sepNavSelRange = "{13594, 52}";
|
||||
sepNavVisRange = "{12984, 1303}";
|
||||
sepNavWindowFrame = "{{86, 104}, {750, 728}}";
|
||||
};
|
||||
};
|
||||
}
|
||||
243
tools/corecompare/osx/corecompare.xcodeproj/project.pbxproj
Normal file
243
tools/corecompare/osx/corecompare.xcodeproj/project.pbxproj
Normal file
@@ -0,0 +1,243 @@
|
||||
// !$*UTF8*$!
|
||||
{
|
||||
archiveVersion = 1;
|
||||
classes = {
|
||||
};
|
||||
objectVersion = 44;
|
||||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
8DD76FB00486AB0100D96B5E /* corecompare.1 in CopyFiles */ = {isa = PBXBuildFile; fileRef = C6A0FF2C0290799A04C91782 /* corecompare.1 */; };
|
||||
EF8E676C0DA22D89000B54B0 /* corecompare.c in Sources */ = {isa = PBXBuildFile; fileRef = EF8E676B0DA22D89000B54B0 /* corecompare.c */; };
|
||||
EF8E67700DA22D92000B54B0 /* corecpu.c in Sources */ = {isa = PBXBuildFile; fileRef = EF8E676E0DA22D92000B54B0 /* corecpu.c */; };
|
||||
EF8E677E0DA231E1000B54B0 /* cpu6502.c in Sources */ = {isa = PBXBuildFile; fileRef = EF8E677D0DA231E1000B54B0 /* cpu6502.c */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXCopyFilesBuildPhase section */
|
||||
8DD76FAF0486AB0100D96B5E /* CopyFiles */ = {
|
||||
isa = PBXCopyFilesBuildPhase;
|
||||
buildActionMask = 8;
|
||||
dstPath = /usr/share/man/man1/;
|
||||
dstSubfolderSpec = 0;
|
||||
files = (
|
||||
8DD76FB00486AB0100D96B5E /* corecompare.1 in CopyFiles */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 1;
|
||||
};
|
||||
/* End PBXCopyFilesBuildPhase section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
8DD76FB20486AB0100D96B5E /* corecompare */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = corecompare; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
C6A0FF2C0290799A04C91782 /* corecompare.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = corecompare.1; sourceTree = "<group>"; };
|
||||
EF8E676B0DA22D89000B54B0 /* corecompare.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = corecompare.c; path = ../src/corecompare.c; sourceTree = SOURCE_ROOT; };
|
||||
EF8E676E0DA22D92000B54B0 /* corecpu.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = corecpu.c; sourceTree = "<group>"; };
|
||||
EF8E676F0DA22D92000B54B0 /* corecpu.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = corecpu.h; sourceTree = "<group>"; };
|
||||
EF8E677C0DA231E1000B54B0 /* cpu6502.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = cpu6502.h; path = ../../../src/cpu/cpu6502.h; sourceTree = SOURCE_ROOT; };
|
||||
EF8E677D0DA231E1000B54B0 /* cpu6502.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = cpu6502.c; path = ../../../src/cpu/cpu6502.c; sourceTree = SOURCE_ROOT; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
8DD76FAD0486AB0100D96B5E /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXFrameworksBuildPhase section */
|
||||
|
||||
/* Begin PBXGroup section */
|
||||
08FB7794FE84155DC02AAC07 /* corecompare */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
08FB7795FE84155DC02AAC07 /* Source */,
|
||||
C6A0FF2B0290797F04C91782 /* Documentation */,
|
||||
1AB674ADFE9D54B511CA2CBB /* Products */,
|
||||
);
|
||||
name = corecompare;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
08FB7795FE84155DC02AAC07 /* Source */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
EF8E67580DA2270E000B54B0 /* src */,
|
||||
);
|
||||
name = Source;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
1AB674ADFE9D54B511CA2CBB /* Products */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
8DD76FB20486AB0100D96B5E /* corecompare */,
|
||||
);
|
||||
name = Products;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
C6A0FF2B0290797F04C91782 /* Documentation */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
C6A0FF2C0290799A04C91782 /* corecompare.1 */,
|
||||
);
|
||||
name = Documentation;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
EF8E67580DA2270E000B54B0 /* src */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
EF8E67770DA231AF000B54B0 /* CPU_JBN */,
|
||||
EF8E676D0DA22D92000B54B0 /* CPU_MTO */,
|
||||
EF8E676B0DA22D89000B54B0 /* corecompare.c */,
|
||||
);
|
||||
name = src;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
EF8E676D0DA22D92000B54B0 /* CPU_MTO */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
EF8E676E0DA22D92000B54B0 /* corecpu.c */,
|
||||
EF8E676F0DA22D92000B54B0 /* corecpu.h */,
|
||||
);
|
||||
name = CPU_MTO;
|
||||
path = ../src/CPU_MTO;
|
||||
sourceTree = SOURCE_ROOT;
|
||||
};
|
||||
EF8E67770DA231AF000B54B0 /* CPU_JBN */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
EF8E677C0DA231E1000B54B0 /* cpu6502.h */,
|
||||
EF8E677D0DA231E1000B54B0 /* cpu6502.c */,
|
||||
);
|
||||
name = CPU_JBN;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* End PBXGroup section */
|
||||
|
||||
/* Begin PBXNativeTarget section */
|
||||
8DD76FA90486AB0100D96B5E /* corecompare */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = 1DEB928508733DD80010E9CD /* Build configuration list for PBXNativeTarget "corecompare" */;
|
||||
buildPhases = (
|
||||
8DD76FAB0486AB0100D96B5E /* Sources */,
|
||||
8DD76FAD0486AB0100D96B5E /* Frameworks */,
|
||||
8DD76FAF0486AB0100D96B5E /* CopyFiles */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
dependencies = (
|
||||
);
|
||||
name = corecompare;
|
||||
productInstallPath = "$(HOME)/bin";
|
||||
productName = corecompare;
|
||||
productReference = 8DD76FB20486AB0100D96B5E /* corecompare */;
|
||||
productType = "com.apple.product-type.tool";
|
||||
};
|
||||
/* End PBXNativeTarget section */
|
||||
|
||||
/* Begin PBXProject section */
|
||||
08FB7793FE84155DC02AAC07 /* Project object */ = {
|
||||
isa = PBXProject;
|
||||
buildConfigurationList = 1DEB928908733DD80010E9CD /* Build configuration list for PBXProject "corecompare" */;
|
||||
compatibilityVersion = "Xcode 3.0";
|
||||
hasScannedForEncodings = 1;
|
||||
mainGroup = 08FB7794FE84155DC02AAC07 /* corecompare */;
|
||||
projectDirPath = "";
|
||||
projectRoot = "";
|
||||
targets = (
|
||||
8DD76FA90486AB0100D96B5E /* corecompare */,
|
||||
);
|
||||
};
|
||||
/* End PBXProject section */
|
||||
|
||||
/* Begin PBXSourcesBuildPhase section */
|
||||
8DD76FAB0486AB0100D96B5E /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
EF8E676C0DA22D89000B54B0 /* corecompare.c in Sources */,
|
||||
EF8E67700DA22D92000B54B0 /* corecpu.c in Sources */,
|
||||
EF8E677E0DA231E1000B54B0 /* cpu6502.c in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXSourcesBuildPhase section */
|
||||
|
||||
/* Begin XCBuildConfiguration section */
|
||||
1DEB928608733DD80010E9CD /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
COPY_PHASE_STRIP = NO;
|
||||
GCC_DYNAMIC_NO_PIC = NO;
|
||||
GCC_ENABLE_FIX_AND_CONTINUE = YES;
|
||||
GCC_MODEL_TUNING = G5;
|
||||
GCC_OPTIMIZATION_LEVEL = 0;
|
||||
INSTALL_PATH = /usr/local/bin;
|
||||
PRODUCT_NAME = corecompare;
|
||||
ZERO_LINK = YES;
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
1DEB928708733DD80010E9CD /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
GCC_MODEL_TUNING = G5;
|
||||
HEADER_SEARCH_PATHS = (
|
||||
../../../src/emulator/,
|
||||
../../../src/,
|
||||
../../../src/cpu/,
|
||||
../src/include/,
|
||||
);
|
||||
INSTALL_PATH = /usr/local/bin;
|
||||
PRODUCT_NAME = corecompare;
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
1DEB928A08733DD80010E9CD /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
PREBINDING = NO;
|
||||
SDKROOT = "$(DEVELOPER_SDK_DIR)/MacOSX10.5.sdk";
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
1DEB928B08733DD80010E9CD /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ARCHS = (
|
||||
ppc,
|
||||
i386,
|
||||
);
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
PREBINDING = NO;
|
||||
SDKROOT = "$(DEVELOPER_SDK_DIR)/MacOSX10.5.sdk";
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
/* End XCBuildConfiguration section */
|
||||
|
||||
/* Begin XCConfigurationList section */
|
||||
1DEB928508733DD80010E9CD /* Build configuration list for PBXNativeTarget "corecompare" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
1DEB928608733DD80010E9CD /* Debug */,
|
||||
1DEB928708733DD80010E9CD /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
1DEB928908733DD80010E9CD /* Build configuration list for PBXProject "corecompare" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
1DEB928A08733DD80010E9CD /* Debug */,
|
||||
1DEB928B08733DD80010E9CD /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
/* End XCConfigurationList section */
|
||||
};
|
||||
rootObject = 08FB7793FE84155DC02AAC07 /* Project object */;
|
||||
}
|
||||
1761
tools/corecompare/src/CPU_MTO/corecpu.c
Executable file
1761
tools/corecompare/src/CPU_MTO/corecpu.c
Executable file
File diff suppressed because it is too large
Load Diff
163
tools/corecompare/src/CPU_MTO/corecpu.h
Executable file
163
tools/corecompare/src/CPU_MTO/corecpu.h
Executable file
@@ -0,0 +1,163 @@
|
||||
/**
|
||||
* CoreCPU - The Quick6502 Project
|
||||
* corecpu.h
|
||||
*
|
||||
* Created by Manoel Trapier on 24/02/08
|
||||
* Copyright 2008 986 Corp. All rights reserved.
|
||||
*
|
||||
* $LastChangedDate: 2008-03-21 01:55:10 +0100 (Ven, 21 mar 2008) $
|
||||
* $Author: godzil $
|
||||
* $HeadURL: svn+ssh://godzil@trac.godzil.net/svn/projects/Quick6502/trunk/corecpu.h $
|
||||
* $Revision: 41 $
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _QUICK6502_CORECPU_H_
|
||||
#define _QUICK6502_CORECPU_H_
|
||||
|
||||
/* M6502 configuration
|
||||
*
|
||||
* Supported DEFINEs :
|
||||
* NO_DECIMAL Quick6502 will not support BDC arithemtic (used for NES)
|
||||
* CMOS_6502 Quick6502 will act as a CMOS 6502 (Not actually used)
|
||||
*
|
||||
*/
|
||||
|
||||
#ifdef CMOS_6502
|
||||
#warning Quick6502 CMOS support is actually desactivated, desactivate it
|
||||
#undef CMOS_6502
|
||||
#endif
|
||||
|
||||
#ifndef NO_DECIMAL
|
||||
#warning Quick6502 have actually no BCD support, fallback to no NO_DECIMAL
|
||||
#define NO_DECIMAL
|
||||
#endif
|
||||
|
||||
|
||||
#include "types.h"
|
||||
|
||||
typedef byte (*quick6502_MemoryReadFunction)(unsigned short addr);
|
||||
typedef void (*quick6502_MemoryWriteFunction)(unsigned short addr, byte value);
|
||||
|
||||
typedef struct quick6502_cpu_
|
||||
{
|
||||
/* 6502 registers */
|
||||
byte reg_A, reg_X, reg_Y;
|
||||
byte reg_P, reg_S;
|
||||
unsigned short reg_PC;
|
||||
|
||||
/* Read/Write memory functions */
|
||||
quick6502_MemoryReadFunction memory_read;
|
||||
quick6502_MemoryWriteFunction memory_write;
|
||||
quick6502_MemoryReadFunction memory_page0_read;
|
||||
quick6502_MemoryWriteFunction memory_page0_write;
|
||||
quick6502_MemoryReadFunction memory_stack_read;
|
||||
quick6502_MemoryWriteFunction memory_stack_write;
|
||||
quick6502_MemoryReadFunction memory_opcode_read;
|
||||
|
||||
/* Timing related */
|
||||
long cycle_done;
|
||||
byte exit_loop;
|
||||
|
||||
/* Other config options */
|
||||
byte running; /* This field is used to prevent cpu free if this cpu is running */
|
||||
|
||||
} quick6502_cpu;
|
||||
|
||||
typedef struct quick6502_cpuconfig_
|
||||
{
|
||||
/* Read/Write memory functions */
|
||||
quick6502_MemoryReadFunction memory_read;
|
||||
quick6502_MemoryWriteFunction memory_write;
|
||||
quick6502_MemoryReadFunction memory_page0_read;
|
||||
quick6502_MemoryWriteFunction memory_page0_write;
|
||||
quick6502_MemoryReadFunction memory_stack_read;
|
||||
quick6502_MemoryWriteFunction memory_stack_write;
|
||||
quick6502_MemoryReadFunction memory_opcode_read;
|
||||
} quick6502_cpuconfig;
|
||||
|
||||
/*** Signal that we can send to the CPU ***/
|
||||
typedef enum
|
||||
{
|
||||
Q6502_NO_SIGNAL = 0,
|
||||
Q6502_IRQ_SIGNAL,
|
||||
Q6502_NMI_SIGNAL,
|
||||
Q6502_STOPLOOP_SIGNAL
|
||||
} quick6502_signal;
|
||||
|
||||
/*** Some 6502 related definitions ***/
|
||||
|
||||
/*** P register flags ***/
|
||||
#define Q6502_N_FLAG 0x80 /* Negavite flag */
|
||||
#define Q6502_V_FLAG 0x40 /* oVerflow flag */
|
||||
#define Q6502_R_FLAG 0x20 /* Not a real flag, but need to be to 1 on PHP */
|
||||
#define Q6502_B_FLAG 0x10 /* Break flag */
|
||||
#define Q6502_D_FLAG 0x08 /* BCD flag */
|
||||
#define Q6502_I_FLAG 0x04 /* IRQ/BRK flag */
|
||||
#define Q6502_Z_FLAG 0x02 /* Zero flag */
|
||||
#define Q6502_C_FLAG 0x01 /* Carry flag */
|
||||
|
||||
/*** Interuption Vectors ***/
|
||||
#define Q6502_NMI_LOW 0xFFFA
|
||||
#define Q6502_NMI_HIGH 0xFFFB
|
||||
#define Q6502_RESET_LOW 0xFFFC
|
||||
#define Q6502_RESET_HIGH 0xFFFD
|
||||
#define Q6502_IRQ_LOW 0xFFFE
|
||||
#define Q6502_IRQ_HIGH 0xFFFF
|
||||
|
||||
/**
|
||||
* Initialise the CPU
|
||||
*
|
||||
* Inputs:
|
||||
*
|
||||
* - CPU Init structure:
|
||||
* +- Memory Read function pointer
|
||||
* +- Memory Write function pointer
|
||||
* +- Fast memory read function pointer (for opcodes read)
|
||||
* +- Fast page 0 function / Read/Write
|
||||
* +- Fast page 1 function / Read/Write
|
||||
*
|
||||
* Output:
|
||||
*
|
||||
* (void *): An opaque pointer to the internal structure of the CPU
|
||||
*
|
||||
*/
|
||||
quick6502_cpu *quick6502_init(quick6502_cpuconfig *config);
|
||||
|
||||
/* Reset the CPU (must be done after init) */
|
||||
void quick6502_reset(quick6502_cpu *cpu);
|
||||
|
||||
/**
|
||||
* Run cpu for at least X cycles
|
||||
*
|
||||
* Output:
|
||||
*
|
||||
* int: (Number of cycle really done) - (Number of cycle asked)
|
||||
*/
|
||||
int quick6502_run(quick6502_cpu *cpu, int cycles);
|
||||
|
||||
/** Loop CPU until explicit quit */
|
||||
void quick6502_loop(quick6502_cpu *cpu);
|
||||
|
||||
/** Run CPU for one instruction */
|
||||
void quick6502_exec(quick6502_cpu *cpu);
|
||||
|
||||
/** Send IRQ/NMI/EXITLOOP signal to CPU */
|
||||
void quick6502_int(quick6502_cpu *cpu, quick6502_signal signal);
|
||||
|
||||
/** Dump CPU State to the given file */
|
||||
void quick6502_dump(quick6502_cpu *cpu, FILE * fp);
|
||||
|
||||
/** Get current instruction name at specified address and put it into buffer */
|
||||
void quick6502_getinstruction(quick6502_cpu *cpu, unsigned short addr, char *buffer);
|
||||
|
||||
/**
|
||||
* Free the CPU
|
||||
*
|
||||
* This function will free the CPU only if it's not currently used, it will
|
||||
* return !0 if everything goes well and 0 if the free is impossible
|
||||
*/
|
||||
int quick6502_free(quick6502_cpu *cpu);
|
||||
|
||||
#endif /* _QUICK6502_CORECPU_H_ */
|
||||
|
||||
220
tools/corecompare/src/corecompare.c
Executable file
220
tools/corecompare/src/corecompare.c
Executable file
@@ -0,0 +1,220 @@
|
||||
/*
|
||||
* corecompare.c
|
||||
* Quick6502
|
||||
*
|
||||
* Created by Manoël Trapier on 18/03/08.
|
||||
* Copyright 2008 986 Corp. All rights reserved.
|
||||
*
|
||||
* Simple tool to compare both core results
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <pthread.h>
|
||||
|
||||
#include "corecpu.h"
|
||||
|
||||
#define BLANK_TIME 5000
|
||||
|
||||
#include "types.h"
|
||||
|
||||
byte mMainMemory[64*1024];
|
||||
byte qMainMemory[64*1024];
|
||||
|
||||
unsigned short ProgramOrg = 0xFFE5;
|
||||
|
||||
byte Program[] = { 0xA9, 0xA5, 0x40, 0x18, 0x78, 0xD8, 0xB8, 0x00, 0xEA, 0xAA, 0xCA, 0xCA, 0xEA, 0x6A, 0x4C, 0xF2, 0xFF, 0xEA, 0x4C, 0xF2, 0xFF, 0xE7, 0xFF, 0xE8, 0xFF, 0xE5, 0xFF };
|
||||
|
||||
unsigned short Smb1Org = 0x8000;
|
||||
#include "smb1.h"
|
||||
#include "nestest.h"
|
||||
|
||||
unsigned short qMemoryReadAddr, mMemoryReadAddr;
|
||||
unsigned short qMemoryWriteAddr, mMemoryWriteAddr;
|
||||
unsigned char qMemoryWriteValue, mMemoryWriteValue;
|
||||
unsigned char qMemoryReadValue, mMemoryReadValue;
|
||||
|
||||
/* Quick6502 related functions and main thread */
|
||||
|
||||
byte ReadMemory(unsigned short addr)
|
||||
{
|
||||
return (qMemoryReadValue = qMainMemory[(qMemoryReadAddr = addr)]);
|
||||
}
|
||||
|
||||
byte ReadMemoryOpCode(unsigned short addr)
|
||||
{
|
||||
return qMainMemory[addr] ;
|
||||
}
|
||||
|
||||
void WriteMemory(unsigned short addr, byte value)
|
||||
{
|
||||
qMemoryWriteValue = value;
|
||||
qMemoryWriteAddr = addr;
|
||||
qMainMemory[addr] = value;
|
||||
}
|
||||
|
||||
|
||||
/* JBN's 6502 CPU related functions */
|
||||
#include <stdint.h>
|
||||
|
||||
#include "cpu6502.h"
|
||||
#include "cyclesTable.h"
|
||||
|
||||
void Wr6502(register uint16_t Addr,register uint8_t Value)
|
||||
{
|
||||
mMemoryWriteValue = Value;
|
||||
mMemoryWriteAddr = Addr;
|
||||
mMainMemory[Addr] = Value;
|
||||
}
|
||||
|
||||
uint8_t Rd6502 (register uint16_t Addr)
|
||||
{
|
||||
return (mMemoryReadValue = mMainMemory[(mMemoryReadAddr = Addr)]);
|
||||
}
|
||||
|
||||
uint8_t Op6502 (register uint16_t Addr)
|
||||
{
|
||||
return mMainMemory[Addr];
|
||||
}
|
||||
|
||||
void debugger_stop ()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
int Exec6502 (nes_cpu6502_t *cpu)
|
||||
{
|
||||
uint8_t opcode;
|
||||
int result;
|
||||
|
||||
/* Manage the Current Opcode. */
|
||||
opcode = cpu->memRead (cpu->pc);
|
||||
result = cpu6502_execOpCode (cpu, opcode);
|
||||
if (result != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Compute the CPU Time taken by the executed opcode. */
|
||||
cpu->nbCycles -= OpcodesCycles[opcode];
|
||||
return result;
|
||||
}
|
||||
|
||||
int main (int argc, const char * argv[]) {
|
||||
char qBuffer[100],
|
||||
mBuffer[100],
|
||||
qBuffer2[100],
|
||||
mBuffer2[100];
|
||||
int result, result2;
|
||||
int count = 0;
|
||||
int x;
|
||||
/* Copy the 6502 program to both CPU's Memory */
|
||||
//memcpy(MainMemory+ ProgramOrg, Program, 0x10000 - ProgramOrg );
|
||||
|
||||
//memcpy(qMainMemory+ Smb1Org, smb1, 0x10000 - Smb1Org );
|
||||
//memcpy(mMainMemory+ Smb1Org, smb1, 0x10000 - Smb1Org );
|
||||
|
||||
memcpy(qMainMemory+ 0xC000, nestest, 0x10000 - 0xC000 );
|
||||
memcpy(mMainMemory+ 0xC000, nestest, 0x10000 - 0xC000 );
|
||||
|
||||
mMainMemory[0x2002] = 0xC0;
|
||||
qMainMemory[0x2002] = 0xC0;
|
||||
|
||||
/* mMainMemory[0x4016] = 0x41;
|
||||
qMainMemory[0x4016] = 0x41;*/
|
||||
|
||||
/* Initialise both core */
|
||||
nes_cpu6502_t mCpu;
|
||||
quick6502_cpu *qCpu;
|
||||
quick6502_cpuconfig CpuConfig;
|
||||
|
||||
/* Init the CPU */
|
||||
CpuConfig.memory_read = ReadMemory;
|
||||
CpuConfig.memory_write = WriteMemory;
|
||||
|
||||
CpuConfig.memory_opcode_read = ReadMemory;
|
||||
CpuConfig.memory_page0_read =
|
||||
CpuConfig.memory_stack_read = NULL;
|
||||
CpuConfig.memory_page0_write =
|
||||
CpuConfig.memory_stack_write = NULL;
|
||||
|
||||
qCpu = quick6502_init(&CpuConfig);
|
||||
if (!qCpu)
|
||||
{
|
||||
printf("CPU initialisation error...");
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
|
||||
mCpu.memRead = Rd6502;
|
||||
mCpu.memWrite = Wr6502;
|
||||
|
||||
quick6502_reset(qCpu);
|
||||
|
||||
cpuReset (&mCpu);
|
||||
|
||||
/* Run each one insruction by one instruction */
|
||||
while(1)
|
||||
{
|
||||
if (count > 19000)
|
||||
{
|
||||
printf("Fire NMI!\n");
|
||||
quick6502_int(qCpu, Q6502_NMI_SIGNAL);
|
||||
NMI_Interrupt (&mCpu);
|
||||
count = 0;
|
||||
}
|
||||
|
||||
mMemoryWriteValue = 0;
|
||||
mMemoryWriteAddr = 0;
|
||||
mMemoryReadValue = 0;
|
||||
mMemoryReadAddr = 0;
|
||||
|
||||
qMemoryWriteValue = 0;
|
||||
qMemoryWriteAddr = 0;
|
||||
qMemoryReadValue = 0;
|
||||
qMemoryReadAddr = 0;
|
||||
|
||||
quick6502_exec(qCpu);
|
||||
Exec6502(&mCpu);
|
||||
sprintf(qBuffer, "A:%02X X:%02X Y:%02X S:%02X P:%02X PC:%04X",
|
||||
qCpu->reg_A, qCpu->reg_X, qCpu->reg_Y, qCpu->reg_S, qCpu->reg_P, qCpu->reg_PC);
|
||||
|
||||
sprintf(mBuffer, "A:%02X X:%02X Y:%02X S:%02X P:%02X PC:%04X",
|
||||
mCpu.a, mCpu.x, mCpu.y, mCpu.R_Stack, mCpu.R_Status, mCpu.pc);
|
||||
|
||||
result = strcmp(qBuffer, mBuffer);
|
||||
sprintf(qBuffer2, "[R:%04X:%02X - W:%04X:%02X]",
|
||||
qMemoryReadAddr,
|
||||
qMemoryReadValue,
|
||||
qMemoryWriteAddr,
|
||||
qMemoryWriteValue);
|
||||
|
||||
sprintf(mBuffer2, "[R:%04X:%02X - W:%04X:%02X]",
|
||||
mMemoryReadAddr,
|
||||
mMemoryReadValue,
|
||||
mMemoryWriteAddr,
|
||||
mMemoryWriteValue);
|
||||
result2 = strcmp(qBuffer2, mBuffer2);
|
||||
|
||||
if ((result != 0) || (result2 != 0))
|
||||
{
|
||||
#if 0
|
||||
for ( x = 0x00; x < 0x100; x++)
|
||||
{
|
||||
printf("%02X|%02X ", ReadMemory(x), Rd6502(x));
|
||||
if ((x&0x0F) == 0x0F)
|
||||
printf("\n");
|
||||
}
|
||||
#endif
|
||||
printf("REGS | %s | %s | %s\n", qBuffer, (result==0)?"===":"/!\\", mBuffer);
|
||||
printf("MEMA | %s | %s | %s\n", qBuffer2, (result2==0)?"===":"/!\\", mBuffer2);
|
||||
}
|
||||
count ++;
|
||||
}
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
2240
tools/corecompare/src/include/nestest.h
Executable file
2240
tools/corecompare/src/include/nestest.h
Executable file
File diff suppressed because it is too large
Load Diff
3730
tools/corecompare/src/include/smb1.h
Executable file
3730
tools/corecompare/src/include/smb1.h
Executable file
File diff suppressed because it is too large
Load Diff
28
tools/corecompare/src/include/types.h
Executable file
28
tools/corecompare/src/include/types.h
Executable file
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Base type definitions - The TI-NESulator Project
|
||||
* types.h - Taken from the Quick6502 project
|
||||
*
|
||||
* Created by Manoel Trapier on 18/09/06.
|
||||
* Copyright 2003-2008 986 Corp. All rights reserved.
|
||||
*
|
||||
* $LastChangedDate: 2008-03-12 12:12:07 +0100 (Mer, 12 mar 2008) $
|
||||
* $Author: godzil $
|
||||
* $HeadURL: svn+ssh://godzil@trac.godzil.net/svn/projects/Quick6502/types.h $
|
||||
* $Revision: 26 $
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef TYPES_H
|
||||
#define TYPES_H
|
||||
|
||||
#ifndef BYTE_TYPE_DEFINED
|
||||
#define BYTE_TYPE_DEFINED
|
||||
typedef unsigned char byte;
|
||||
#endif
|
||||
|
||||
typedef unsigned char bool;
|
||||
|
||||
#define true (0)
|
||||
#define false (!true)
|
||||
|
||||
#endif
|
||||
15
unix/SConstruct
Normal file
15
unix/SConstruct
Normal file
@@ -0,0 +1,15 @@
|
||||
# -*- python -*-
|
||||
|
||||
##############
|
||||
# Nes program
|
||||
#
|
||||
#
|
||||
|
||||
Program('nes', ['../src/main.c', '../src/debug.c', '../src/cpu/cpu6502.c',
|
||||
'../src/emulator/emulator.c', '../src/emulator/memory.c',
|
||||
'../src/emulator/ppu.c', '../src/emulator/apu.c',
|
||||
'../src/emulator/debugger.c', '../src/emulator/paddle.c'],
|
||||
CPPPATH=['../src', '../src/cpu', '../src/emulator'],
|
||||
CCFLAGS='-g -Wall `sdl-config --cflags`',
|
||||
LIBS=['SDL'],
|
||||
LIBPATH='/usr/lib')
|
||||
Reference in New Issue
Block a user