Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
akatsuki105 committed Apr 13, 2021
1 parent 58066c4 commit 0076783
Show file tree
Hide file tree
Showing 6 changed files with 104 additions and 48 deletions.
8 changes: 1 addition & 7 deletions cmd/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -123,13 +123,7 @@ type Emulator struct {
}

func (e *Emulator) Update() error {
defer func() {
if err := recover(); err != nil {
fmt.Fprintf(os.Stderr, "crash in emulation: %s in 0x%08x\n", err, e.gba.PC())
e.gba.Exit("")
panic("")
}
}()
defer e.gba.PanicHandler(true)
e.gba.Update()
if e.gba.DoSav && e.gba.Frame%60 == 0 {
e.writeSav()
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,5 @@ go 1.16

require (
github.com/anthonynsimon/bild v0.13.0 // indirect
github.com/hajimehoshi/ebiten/v2 v2.0.6
github.com/hajimehoshi/ebiten/v2 v2.0.8
)
3 changes: 3 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,12 @@ github.com/gofrs/flock v0.8.0/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14j
github.com/hajimehoshi/bitmapfont/v2 v2.1.0/go.mod h1:2BnYrkTQGThpr/CY6LorYtt/zEPNzvE/ND69CRTaHMs=
github.com/hajimehoshi/ebiten/v2 v2.0.6 h1:sHNymgI+q80xasP69oFyrpup6r2qCNsKxqwsGEh6PWE=
github.com/hajimehoshi/ebiten/v2 v2.0.6/go.mod h1:uS3OjMW3f2DRDMtWoIF7yMMmrMkv+fZ6pXcwR1pfA0Y=
github.com/hajimehoshi/ebiten/v2 v2.0.8 h1:+LPEcUhsLS3swxf2LtBb2V1TO72YoGPArWPqxzXEDYI=
github.com/hajimehoshi/ebiten/v2 v2.0.8/go.mod h1:uS3OjMW3f2DRDMtWoIF7yMMmrMkv+fZ6pXcwR1pfA0Y=
github.com/hajimehoshi/file2byteslice v0.0.0-20200812174855-0e5e8a80490e/go.mod h1:CqqAHp7Dk/AqQiwuhV1yT2334qbA/tFWQW0MD2dGqUE=
github.com/hajimehoshi/go-mp3 v0.3.1/go.mod h1:qMJj/CSDxx6CGHiZeCgbiq2DSUkbK0UbtXShQcnfyMM=
github.com/hajimehoshi/oto v0.6.1/go.mod h1:0QXGEkbuJRohbJaxr7ZQSxnju7hEhseiPx2hrh6raOI=
github.com/hajimehoshi/oto v0.6.8 h1:yRb3EJQ4lAkBgZYheqmdH6Lr77RV9nSWFsK/jwWdTNY=
github.com/hajimehoshi/oto v0.6.8/go.mod h1:0QXGEkbuJRohbJaxr7ZQSxnju7hEhseiPx2hrh6raOI=
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
Expand Down
117 changes: 78 additions & 39 deletions pkg/gba/apu.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ import (
"math"
"mettaur/pkg/ram"
"mettaur/pkg/util"
"time"

"github.com/hajimehoshi/ebiten/v2/audio"
)

var waveSamples byte
Expand Down Expand Up @@ -36,6 +39,12 @@ const (
var dutyLookUp = [4]float64{0.125, 0.25, 0.5, 0.75}
var dutyLookUpi = [4]float64{0.875, 0.75, 0.5, 0.25}

type APU struct {
context *audio.Context
player *audio.Player
chans [4]*SoundChan
}

type SoundChan struct {
phase bool
lfsr uint16
Expand All @@ -45,6 +54,29 @@ type SoundChan struct {
envTime float64
}

func newAPU() *APU {
stream = make([]byte, BUFF_SAMPLES*2)
context := audio.NewContext(SND_FREQUENCY)
player := audio.NewPlayerFromBytes(context, stream)
return &APU{
context: context,
player: player,
chans: [4]*SoundChan{&SoundChan{}, &SoundChan{}, &SoundChan{}, &SoundChan{}},
}
}

func (g *GBA) playSound() {
go func() {
for range time.Tick(time.Second / 120) {
g.soundMix()
if !g.apu.player.IsPlaying() {
g.apu.player.Rewind()
go g.apu.player.Play()
}
}
}()
}

func (g *GBA) squareSample(ch int) int8 {
cntx := g._getRAM(ram.SOUNDCNT_X)
if !util.Bit(cntx, ch) {
Expand Down Expand Up @@ -77,8 +109,8 @@ func (g *GBA) squareSample(ch int) int8 {

// Length reached check (if so, just disable the channel and return silence)
if (ch == 0 && util.Bit(g._getRAM(ram.SOUND1CNT_X), 14)) || (ch == 1 && util.Bit(g._getRAM(ram.SOUND2CNT_H), 14)) {
g.apu[ch].lengthTime += SAMPLE_TIME
if g.apu[ch].lengthTime >= length {
g.apu.chans[ch].lengthTime += SAMPLE_TIME
if g.apu.chans[ch].lengthTime >= length {
g.disableSoundChan(ch)
return 0
}
Expand All @@ -88,9 +120,9 @@ func (g *GBA) squareSample(ch int) int8 {
sweepTime := (g._getRAM(ram.SOUND1CNT_L) >> 4) & 0b111
sweepInterval := 0.0078 * float64(sweepTime+1) // Frquency sweep change interval in seconds
if ch == 0 {
g.apu[0].sweepTime += SAMPLE_TIME
if g.apu[0].sweepTime >= sweepInterval {
g.apu[0].sweepTime -= sweepInterval
g.apu.chans[0].sweepTime += SAMPLE_TIME
if g.apu.chans[0].sweepTime >= sweepInterval {
g.apu.chans[0].sweepTime -= sweepInterval
sweepShift := byte(g._getRAM(ram.SOUND1CNT_L) & 0b111)
if sweepShift > 0 {
disp := uint32(freqHz >> sweepShift)
Expand All @@ -114,9 +146,9 @@ func (g *GBA) squareSample(ch int) int8 {

envelope := uint16((g._getRAM(toneAddr) >> 12) & 0xf)
if envStep > 0 {
g.apu[ch].envTime += SAMPLE_TIME
if g.apu[ch].envTime >= envelopeInterval {
g.apu[ch].envTime -= envelopeInterval
g.apu.chans[ch].envTime += SAMPLE_TIME
if g.apu.chans[ch].envTime >= envelopeInterval {
g.apu.chans[ch].envTime -= envelopeInterval

tone := uint16(g._getRAM(toneAddr))
if util.Bit(tone, 11) {
Expand All @@ -135,25 +167,25 @@ func (g *GBA) squareSample(ch int) int8 {
}

// Phase change (when the wave goes from Low to High or High to Low, the Square Wave pattern)
duty := (g._getRAM(toneAddr) >> 6) & 0b111
g.apu[ch].samples++
if g.apu[ch].phase {
duty := (g._getRAM(toneAddr) >> 6) & 0b11
g.apu.chans[ch].samples++
if g.apu.chans[ch].phase {
// 1 -> 0
phaseChange := cycleSamples * dutyLookUp[duty]
if g.apu[ch].samples > phaseChange {
g.apu[ch].samples -= phaseChange
g.apu[ch].phase = false
if g.apu.chans[ch].samples > phaseChange {
g.apu.chans[ch].samples -= phaseChange
g.apu.chans[ch].phase = false
}
} else {
// 0 -> 1
phaseChange := cycleSamples * dutyLookUpi[duty]
if g.apu[ch].samples > phaseChange {
g.apu[ch].samples -= phaseChange
g.apu[ch].phase = true
if g.apu.chans[ch].samples > phaseChange {
g.apu.chans[ch].samples -= phaseChange
g.apu.chans[ch].phase = true
}
}

if g.apu[ch].phase {
if g.apu.chans[ch].phase {
return int8((float64(envelope) / 15) * PSG_MAX)
}
return int8((float64(envelope) / 15) * PSG_MIN)
Expand Down Expand Up @@ -186,16 +218,16 @@ func (g *GBA) waveSample() int8 {

// Length reached check (if so, just disable the channel and return silence)
if util.Bit(cntx, 14) {
g.apu[2].lengthTime += SAMPLE_TIME
if g.apu[2].lengthTime >= length {
g.apu.chans[2].lengthTime += SAMPLE_TIME
if g.apu.chans[2].lengthTime >= length {
g.disableSoundChan(2)
return 0
}
}

g.apu[2].samples++
if g.apu[2].samples >= cycleSamples {
g.apu[2].samples -= cycleSamples
g.apu.chans[2].samples++
if g.apu.chans[2].samples >= cycleSamples {
g.apu.chans[2].samples -= cycleSamples

waveSamples--
if waveSamples > 0 {
Expand Down Expand Up @@ -253,8 +285,8 @@ func (g *GBA) noiseSample() int8 {

// Length reached check (if so, just disable the channel and return silence)
if util.Bit(cnth, 14) {
g.apu[3].lengthTime += SAMPLE_TIME
if g.apu[3].lengthTime >= length {
g.apu.chans[3].lengthTime += SAMPLE_TIME
if g.apu.chans[3].lengthTime >= length {
g.disableSoundChan(3)
return 0
}
Expand All @@ -267,9 +299,9 @@ func (g *GBA) noiseSample() int8 {
// Envelope volume
envelope := (cntl >> 12) & 0xf
if envStep != 0 {
g.apu[3].envTime += SAMPLE_TIME
if g.apu[3].envTime >= envelopeInterval {
g.apu[3].envTime -= envelopeInterval
g.apu.chans[3].envTime += SAMPLE_TIME
if g.apu.chans[3].envTime >= envelopeInterval {
g.apu.chans[3].envTime -= envelopeInterval

if util.Bit(cntl, 11) {
if envelope < 0xf {
Expand All @@ -289,17 +321,17 @@ func (g *GBA) noiseSample() int8 {
// Numbers of samples that a single cycle (pseudo-random noise value) takes at output sample rate
cycleSamples := SND_FREQUENCY / frequency

carry := byte(g.apu[3].lfsr & 0b1)
g.apu[3].samples++
if g.apu[3].samples >= cycleSamples {
g.apu[3].samples -= cycleSamples
g.apu[3].lfsr >>= 1
carry := byte(g.apu.chans[3].lfsr & 0b1)
g.apu.chans[3].samples++
if g.apu.chans[3].samples >= cycleSamples {
g.apu.chans[3].samples -= cycleSamples
g.apu.chans[3].lfsr >>= 1

high := uint16(byte(g.apu[3].lfsr&1) ^ carry)
high := uint16(byte(g.apu.chans[3].lfsr&1) ^ carry)
if util.Bit(cnth, 3) {
g.apu[3].lfsr |= (high << 6)
g.apu.chans[3].lfsr |= (high << 6)
} else {
g.apu[3].lfsr |= (high << 14)
g.apu.chans[3].lfsr |= (high << 14)
}
}

Expand Down Expand Up @@ -335,10 +367,16 @@ func (g *GBA) soundBufferWrap() {
}

var sndBuffer [BUFF_SAMPLES]int16
var stream []byte

func (g *GBA) soundMix() {
for i := 0; i < 4; i++ {
// TODO
for i := 0; i < len(sndBuffer); i += 4 {
stream[i+0] = byte(sndBuffer[sndCurPlay&BUFF_SAMPLES_MSK] << 6)
stream[i+1] = byte((sndBuffer[sndCurPlay&BUFF_SAMPLES_MSK] << 6) >> 8)
sndCurPlay++
stream[i+2] = byte(sndBuffer[sndCurPlay&BUFF_SAMPLES_MSK] << 6)
stream[i+3] = byte((sndBuffer[sndCurPlay&BUFF_SAMPLES_MSK] << 6) >> 8)
sndCurPlay++
}

// Avoid desync between the Play cursor and the Write cursor
Expand Down Expand Up @@ -421,12 +459,13 @@ func (g *GBA) clip(val int32) int16 {
}

func (g *GBA) soundClock(cycles uint32) {
defer g.PanicHandler(true)
sndCycles += cycles

sampPcmL, sampPcmR := int16(0), int16(0)

cnth := uint16(g._getRAM(ram.SOUNDCNT_H))
sampCh4, sampCh5 := (int16(fifoASamp)<<1)>>^(int16(cnth)&4), (int16(fifoBSamp)<<1)>>^(int16(cnth)&8)
sampCh4, sampCh5 := (int16(fifoASamp)<<1)>>^(cnth&4), (int16(fifoBSamp)<<1)>>^(cnth&8)

// Left
if util.Bit(cnth, 9) {
Expand Down
17 changes: 17 additions & 0 deletions pkg/gba/debug.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import (
"fmt"
"mettaur/pkg/ram"
"mettaur/pkg/util"
"os"
"runtime"
)

const (
Expand Down Expand Up @@ -331,3 +333,18 @@ func (ih IRQHistory) String() string {
}
return fmt.Sprintf("IRQ(%s): 0x%08x -> 0x%08x on %s", ih.irq, ih.start, ih.returnTo, mode)
}

func (g *GBA) PanicHandler(stack bool) {
if err := recover(); err != nil {
fmt.Fprintf(os.Stderr, "crash in emulation: %s in 0x%08x\n", err, g.PC())
for depth := 0; ; depth++ {
_, file, line, ok := runtime.Caller(depth)
if !ok {
break
}
fmt.Printf("======> %d: %v:%d\n", depth, file, line)
}
g.Exit("")
panic("")
}
}
5 changes: 4 additions & 1 deletion pkg/gba/gba.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ type GBA struct {
dma [4]*DMA
joypad joypad.Joypad
DoSav bool
apu [4]*SoundChan
apu *APU
}

type Pipe struct {
Expand All @@ -77,8 +77,10 @@ func New(src []byte) *GBA {
CartHeader: cart.New(src),
RAM: *ram.New(src),
dma: NewDMA(),
apu: newAPU(),
}
g._setRAM16(ram.KEYINPUT, 0x3ff)
g.playSound()
return g
}

Expand Down Expand Up @@ -212,6 +214,7 @@ func (g *GBA) scanline() {
}
}

g.soundClock(1232)
g.line++
}

Expand Down

0 comments on commit 0076783

Please sign in to comment.