move nes into the sources/NES directory

This commit is contained in:
jb
2009-02-05 08:24:50 +00:00
commit 586150bf93
66 changed files with 21595 additions and 0 deletions

BIN
data/MegaMan.nes Normal file

Binary file not shown.

BIN
data/dk1.nes Normal file

Binary file not shown.

BIN
data/dk2.nes Normal file

Binary file not shown.

BIN
data/dk3.nes Normal file

Binary file not shown.

BIN
data/smb1.nes Normal file

Binary file not shown.

Binary file not shown.

View 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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

Binary file not shown.

View 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

Binary file not shown.

1106
docs/nesdoc1.txt Normal file

File diff suppressed because it is too large Load Diff

865
docs/nesdoc2.txt Normal file
View 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é ---------------------------

Binary file not shown.

View 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
View 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

Binary file not shown.

28
osx/Info.plist Normal file
View 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
View 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
View 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;
}

File diff suppressed because it is too large Load Diff

View 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}";
};
};
}

View 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
View 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

File diff suppressed because it is too large Load Diff

94
src/cpu/cpu6502.h Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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", &param);
PPU_ShowTiles (param);
}
break;
case 'c':
PPU_ClearScreen ();
break;
case 'm':
param = 0;
sscanf (buffer+1, "%hx", &param);
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", &param);
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", &param);
fprintf (stdout, "Set PC to: %x\n", param);
emul.cpu.pc = param;
break;
case '=':
param = 0;
/* Read the Address. */
sscanf (buffer+1, "%hx", &param);
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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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

File diff suppressed because it is too large Load Diff

View 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}}";
};
};
}

View 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 */;
}

File diff suppressed because it is too large Load Diff

View 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_ */

View 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;
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View 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
View 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')