Skip to content

Commit 2249b25

Browse files
committed
Initial release.
First milestone! You can have a basic walkthrough experience now! Signed-off-by: net2cn <mcopener@gmail.com>
1 parent 74665b5 commit 2249b25

File tree

8 files changed

+203
-33
lines changed

8 files changed

+203
-33
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,6 @@
22
/.VSCodeCounter
33
/roms
44
/debug
5+
/release
56
go.sum
67
__debug_bin

README.md

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,16 @@ Yet another NES emulator written in Golang.
44
Currently working in progress.
55

66
## Current status:
7-
![SMB_Title](./img/screenshot_20200731154145.png)
7+
CPU and PPU are implemented, as well as the mapper 0. Also, controller 0 is implemented.
8+
9+
But no audio support yet.
10+
11+
![SMB_Title](./img/screenshot_20200731221629.png)
12+
13+
## Usage
14+
```
15+
GoNES -file [NES_ROM_file]
16+
```
817

918
Reference: http://wiki.nesdev.com/
1019

File renamed without changes.

img/screenshot_20200731221629.png

48.6 KB
Loading

main.go

Lines changed: 111 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
11
package main
22

33
import (
4+
"flag"
45
"fmt"
6+
"log"
57
"os"
8+
"runtime/pprof"
69
"sort"
710
"strconv"
811
"time"
@@ -16,13 +19,14 @@ import (
1619

1720
// SDL2 variables
1821
var windowTitle string = "GoNES SDL2"
19-
var fontPath string = "./ui/assets/UbuntuMono-R.ttf" // Man, do not use a variable-width font! It looks too ugly with that!
22+
var fontPath string = "./assets/UbuntuMono-R.ttf" // Man, do not use a variable-width font! It looks too ugly with that!
2023
var fontSize int = 15
2124
var windowWidth, windowHeight int32 = 680, 480
2225

2326
// Timer.
2427
var startTime time.Time = time.Now()
2528
var endTime time.Time = time.Now()
29+
var elapsedTime int64 = 0
2630

2731
type debugger struct {
2832
bus *nes.Bus
@@ -267,26 +271,66 @@ func (debug *debugger) Update(elapsedTime int64) bool {
267271
// Swap screen and buffer (present),
268272
// Clear buffer for next round.
269273

270-
// Get user inputs.
274+
// Get NES controller inputs.
275+
keyState := sdl.GetKeyboardState()
276+
277+
debug.bus.Controller[0] = 0x00
278+
if keyState[sdl.SCANCODE_X] != 0 {
279+
debug.bus.Controller[0] |= 0x80
280+
} else {
281+
debug.bus.Controller[0] |= 0x00
282+
}
283+
284+
if keyState[sdl.SCANCODE_Z] != 0 {
285+
debug.bus.Controller[0] |= 0x40
286+
} else {
287+
debug.bus.Controller[0] |= 0x00
288+
}
289+
290+
if keyState[sdl.SCANCODE_A] != 0 {
291+
debug.bus.Controller[0] |= 0x20
292+
} else {
293+
debug.bus.Controller[0] |= 0x00
294+
}
295+
296+
if keyState[sdl.SCANCODE_S] != 0 {
297+
debug.bus.Controller[0] |= 0x10
298+
} else {
299+
debug.bus.Controller[0] |= 0x00
300+
}
301+
302+
if keyState[sdl.SCANCODE_UP] != 0 {
303+
debug.bus.Controller[0] |= 0x08
304+
} else {
305+
debug.bus.Controller[0] |= 0x00
306+
}
307+
308+
if keyState[sdl.SCANCODE_DOWN] != 0 {
309+
debug.bus.Controller[0] |= 0x04
310+
} else {
311+
debug.bus.Controller[0] |= 0x00
312+
}
313+
314+
if keyState[sdl.SCANCODE_LEFT] != 0 {
315+
debug.bus.Controller[0] |= 0x02
316+
} else {
317+
debug.bus.Controller[0] |= 0x00
318+
}
319+
320+
if keyState[sdl.SCANCODE_RIGHT] != 0 {
321+
debug.bus.Controller[0] |= 0x01
322+
} else {
323+
debug.bus.Controller[0] |= 0x00
324+
}
325+
326+
// Get debugger inputs.
271327
for event := sdl.PollEvent(); event != nil; event = sdl.PollEvent() {
272328
switch t := event.(type) {
273329
case *sdl.QuitEvent:
274330
return false
275331
case *sdl.KeyboardEvent:
276332
if !debug.inputLock {
277333
switch t.Keysym.Sym {
278-
// NES controller
279-
// Bugged...
280-
case sdl.K_s:
281-
debug.bus.Controller[0] |= 0x10
282-
case sdl.K_UP:
283-
debug.bus.Controller[0] |= 0x08
284-
case sdl.K_DOWN:
285-
debug.bus.Controller[0] |= 0x04
286-
case sdl.K_LEFT:
287-
debug.bus.Controller[0] |= 0x02
288-
case sdl.K_RIGHT:
289-
debug.bus.Controller[0] |= 0x01
290334
// Debug utility.
291335
case sdl.K_c:
292336
// Golang's do while.
@@ -321,8 +365,10 @@ func (debug *debugger) Update(elapsedTime int64) bool {
321365

322366
// Anti-jittering
323367
if t.Repeat > 0 {
368+
// Held.
324369
debug.inputLock = false
325370
} else {
371+
// Pressed once.
326372
if t.State == sdl.RELEASED {
327373
debug.inputLock = false
328374
} else if t.State == sdl.PRESSED {
@@ -337,7 +383,7 @@ func (debug *debugger) Update(elapsedTime int64) bool {
337383
if debug.residualTime > 0 {
338384
debug.residualTime -= elapsedTime
339385
} else {
340-
debug.residualTime += 1000/60 - elapsedTime
386+
debug.residualTime += 1000000/60 - elapsedTime
341387
// Golang's do while.
342388
for done := true; done; done = debug.bus.PPU.FrameComplete != true {
343389
debug.bus.Clock()
@@ -389,18 +435,23 @@ func (debug *debugger) Update(elapsedTime int64) bool {
389435
debug.drawDMA(416, 72, 25)
390436

391437
// Draw key hints.
392-
debug.drawString(0, 362, "SPACE - Run/stop", &sdl.Color{R: 0, G: 255, B: 0, A: 0})
393-
debug.drawString(0, 372, "R - Reset", &sdl.Color{R: 0, G: 255, B: 0, A: 0})
394-
debug.drawString(0, 382, "F - Step one frame", &sdl.Color{R: 0, G: 255, B: 0, A: 0})
395-
debug.drawString(0, 392, "C - Step one instruction", &sdl.Color{R: 0, G: 255, B: 0, A: 0})
396-
debug.drawString(0, 402, "D - Dump screen", &sdl.Color{R: 0, G: 255, B: 0, A: 0})
397-
debug.drawString(0, 412, "P - Change palette", &sdl.Color{R: 0, G: 255, B: 0, A: 0})
398-
399-
fps := 0
400-
if elapsedTime != 0 {
401-
fps = int(1000 / elapsedTime)
402-
}
403-
debug.drawString(612, 2, "FPS: "+strconv.Itoa(fps), &sdl.Color{R: 0, G: 255, B: 0, A: 0})
438+
debug.drawString(2, 306, "NES", &sdl.Color{R: 255, G: 255, B: 0, A: 0})
439+
debug.drawString(2, 316, "Z - A", &sdl.Color{R: 0, G: 255, B: 0, A: 0})
440+
debug.drawString(2, 326, "X - B", &sdl.Color{R: 0, G: 255, B: 0, A: 0})
441+
debug.drawString(2, 336, "A - Select", &sdl.Color{R: 0, G: 255, B: 0, A: 0})
442+
debug.drawString(2, 346, "S - Start", &sdl.Color{R: 0, G: 255, B: 0, A: 0})
443+
debug.drawString(2, 356, "UP - Up", &sdl.Color{R: 0, G: 255, B: 0, A: 0})
444+
debug.drawString(2, 366, "DOWN - Down", &sdl.Color{R: 0, G: 255, B: 0, A: 0})
445+
debug.drawString(2, 376, "LEFT - Left", &sdl.Color{R: 0, G: 255, B: 0, A: 0})
446+
debug.drawString(2, 386, "RIGHT - Right", &sdl.Color{R: 0, G: 255, B: 0, A: 0})
447+
448+
debug.drawString(2, 406, "Debugger", &sdl.Color{R: 255, G: 255, B: 0, A: 0})
449+
debug.drawString(2, 416, "SPACE - Run/stop", &sdl.Color{R: 0, G: 255, B: 0, A: 0})
450+
debug.drawString(2, 426, "R - Reset", &sdl.Color{R: 0, G: 255, B: 0, A: 0})
451+
debug.drawString(2, 436, "F - Step one frame", &sdl.Color{R: 0, G: 255, B: 0, A: 0})
452+
debug.drawString(2, 446, "C - Step one instruction", &sdl.Color{R: 0, G: 255, B: 0, A: 0})
453+
debug.drawString(2, 456, "D - Dump screen", &sdl.Color{R: 0, G: 255, B: 0, A: 0})
454+
debug.drawString(2, 466, "P - Change palette", &sdl.Color{R: 0, G: 255, B: 0, A: 0})
404455

405456
// Swap buffer and present our rendered content.
406457
debug.window.UpdateSurface()
@@ -414,28 +465,56 @@ func (debug *debugger) Update(elapsedTime int64) bool {
414465
}
415466

416467
func (debug *debugger) Start() {
417-
elapsedTime := startTime.Sub(endTime).Milliseconds()
468+
var passedTime int64 = 0
469+
var passedFrame int64 = 0
418470

419471
running := true
420472
for running {
421473
startTime = time.Now()
422474
running = debug.Update(elapsedTime)
423475
endTime = time.Now()
424-
elapsedTime = endTime.Sub(startTime).Milliseconds()
476+
elapsedTime = endTime.Sub(startTime).Microseconds()
477+
478+
passedTime += elapsedTime
479+
passedFrame++
480+
if passedTime >= 1000000 {
481+
debug.window.SetTitle(windowTitle + " FPS: " + strconv.Itoa(int(1000000/(passedTime/passedFrame))))
482+
passedTime = 0
483+
passedFrame = 0
484+
}
425485
}
426-
427486
}
428487

429488
func main() {
430489
fmt.Println(windowTitle)
431-
432490
// I really enjoy its graphics. I mean the anime movie.
433491
fmt.Println("HELLO WORLD -ALLTALE-")
434492
fmt.Println("With programming we have god's hand.")
435493

494+
// Read flags
495+
var file = flag.String("file", "", "NES ROM file")
496+
var cpuprofile = flag.String("cpuprofile", "", "Write cpu profile to file")
497+
498+
flag.Parse()
499+
500+
// Handle flags
501+
if *file == "" {
502+
fmt.Println("Please specify a NES ROM file.")
503+
os.Exit(1)
504+
}
505+
506+
if *cpuprofile != "" {
507+
f, err := os.Create(*cpuprofile)
508+
if err != nil {
509+
log.Fatal(err)
510+
}
511+
pprof.StartCPUProfile(f)
512+
defer pprof.StopCPUProfile()
513+
}
514+
436515
// Construct a debugger instance.
437516
debug := debugger{}
438-
err := debug.Construct("./roms/smb.nes", windowWidth, windowHeight)
517+
err := debug.Construct(*file, windowWidth, windowHeight)
439518
if err != nil {
440519
return
441520
}

test/oam_stress.nes

40 KB
Binary file not shown.

test/oam_stress.txt

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
NES OAM Stress Test
2+
-------------------
3+
Thoroughly tests OAM address ($2003) and read/write ($2004). On an NTSC
4+
NES, this passes only for one of the four random PPU-CPU
5+
synchronizations at power/reset. Test takes about 30 seconds, unless it
6+
fails.
7+
8+
This test randomly sets the address, then randomly either writes a
9+
random number of random bytes, or reads from the current address a
10+
random number of times and verifies that it matches what's expected. It
11+
does this for tens of seconds (refreshing OAM periodically so it doesn't
12+
fade). Once done, it verifies that all bytes in OAM match what's
13+
expected.
14+
15+
Expected behavior:
16+
17+
$2003 write sets OAM address.
18+
19+
$2004 write sets byte at current OAM address to byte written, then
20+
increments OAM address.
21+
22+
$2004 read gives byte at current OAM address, without modifying OAM
23+
address.
24+
25+
26+
Flashes, clicks, other glitches
27+
-------------------------------
28+
Some tests might need to turn the screen off and on, or cause slight
29+
audio clicks. This does not indicate failure, and should be ignored.
30+
Only the test result reported at the end is important, unless stated
31+
otherwise.
32+
33+
34+
Text output
35+
-----------
36+
Tests generally print information on screen. They also output the same
37+
text as a zero-terminted string beginning at $6004, allowing examination
38+
of output in an NSF player, or a NES emulator without a working PPU. The
39+
tests also work properly if the PPU doesn't set the VBL flag properly or
40+
doesn't implement it at all.
41+
42+
The final result is displayed and also written to $6000. Before the test
43+
starts, $80 is written there so you can tell when it's done. If a test
44+
needs the NES to be reset, it writes $81 there (emulator should wait a
45+
couple of frames after seeing $81). In addition, $DE $B0 $G1 is written
46+
to $6001-$6003 to allow an emulator to detect when a test is being run,
47+
as opposed to some other NES program. In NSF builds, the final result is
48+
also reported via a series of beeps (see below).
49+
50+
See the source code for more information about a particular test and why
51+
it might be failing. Each test has comments and correct output at the
52+
top.
53+
54+
55+
NSF versions
56+
------------
57+
Many NSF-based tests require that the NSF player either not interrupt
58+
the init routine with the play routine, or if it does, not interrupt the
59+
play routine again if it hasn't returned yet. This is because many tests
60+
need to run for a while without returning.
61+
62+
NSF versions also make periodic clicks to avoid the NSF player from
63+
thinking the track is silent and thus ending the track before it's done
64+
testing.
65+
66+
In addition to the other text output methods described above, NSF builds
67+
report essential information bytes audibly, including the final result.
68+
A byte is reported as a series of tones. The code is in binary, with a
69+
low tone for 0 and a high tone for 1, and with leading zeroes skipped.
70+
The first tone is always a zero. A final code of 0 means passed, 1 means
71+
failure, and 2 or higher indicates a specific reason as listed in the
72+
source code by the corresponding set_code line. Examples:
73+
74+
Tones Binary Decimal Meaning
75+
- - - - - - - - - - - - - - - - - - - -
76+
low 0 0 passed
77+
low high 01 1 failed
78+
low high low 010 2 error 2
79+
80+
--
81+
Shay Green <gblargg@gmail.com>

0 commit comments

Comments
 (0)