Skip to content

Commit

Permalink
WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
196Ikuchil committed Mar 16, 2020
1 parent 3044597 commit 50f7a6e
Show file tree
Hide file tree
Showing 12 changed files with 579 additions and 7 deletions.
2 changes: 2 additions & 0 deletions index.html
Expand Up @@ -61,6 +61,8 @@
<li>→ →</li>
</ul>
</div>
<script type="module" src="./src/nes/webaudio/oscillator.js"></script>
<script type="module" src="./src/nes/webaudio/noise.js"></script>
<script type="module" src="./init.js"></script>
<script type="module" src="./main.js"></script>
<script type="module">
Expand Down
8 changes: 6 additions & 2 deletions main.js
@@ -1,4 +1,4 @@

import Oscillator from './src/nes/webaudio/oscillator.js'
let buf = null

const convertKeyCode = (key) => {
Expand Down Expand Up @@ -38,10 +38,14 @@ const startArrayBuf = (arrayBuf) => {
const run = Module.cwrap('run', null, ['number', 'number'])
const canvas = document.querySelector('canvas')
const ctx = canvas.getContext('2d')
if (Module.NES) {
Module.NES.oscs.forEach(o => o.close());
}
Module.NES = {
ctx,
canvas,
image: ctx.createImageData(256, 240),
oscs: [new Oscillator(), new Oscillator()],
}
canvas.width = 256
canvas.height = 240
Expand All @@ -58,7 +62,7 @@ const startArrayBuf = (arrayBuf) => {
}

// called from html
export const start = async (rom = './roms/nestest.nes') => {
export const start = async (rom = './roms/apu/test_apu_env.nes') => {
const res = await fetch(rom);
const arrayBuf = await res.arrayBuffer();
startArrayBuf(arrayBuf);
Expand Down
Binary file added roms/apu/test_apu_env.nes
Binary file not shown.
18 changes: 18 additions & 0 deletions src/externs/lib.js
Expand Up @@ -4,4 +4,22 @@ mergeInto(LibraryManager.library, {
Module.NES.image.data.set(Module.NES.buf);
Module.NES.ctx.putImageData(Module.NES.image, 0, 0);
},
start_oscillator: function (index) {
Module.NES.oscs[index].start()
},
stop_oscillator: function (index) {
Module.NES.oscs[index].stop()
},
set_oscillator_frequency: function (index, freq) {
Module.NES.oscs[index].setFrequency(freq)
},
change_oscillator_frequency: function (index, freq) {
Module.NES.oscs[index].changeFrequency(freq)
},
set_oscillator_volume: function (index, volume) {
Module.NES.oscs[index].setVolume(volume)
},
set_oscillator_duty: function (index, duty) {
Module.NES.oscs[index].setDuty(duty)
}
});
11 changes: 11 additions & 0 deletions src/nes/apu/constants.rs
@@ -0,0 +1,11 @@
pub const CPU_CLOCK: usize = 1789772;

pub const DIVIDE_COUNT_FOR_240HZ: u16 = 7457;

// ref. http://pgate1.at-ninja.jp/NES_on_FPGA/nes_apu.htm
pub const COUNTER_TABLE: &'static [u8] = &[0x0A, 0xFE, 0x14, 0x02, 0x28, 0x04, 0x50, 0x06, 0xA0,
0x08, 0x3C, 0x0A, 0x0E, 0x0C, 0x1A, 0x0E, 0x0C, 0x10,
0x18, 0x12, 0x30, 0x14, 0x60, 0x16, 0xC0, 0x18, 0x48,
0x1A, 0x10, 0x1C, 0x20, 0x1E];

pub const GROBAL_GAIN: f32 = 0.1;
132 changes: 132 additions & 0 deletions src/nes/apu/mod.rs
@@ -0,0 +1,132 @@
mod square;
mod constants;

use self::constants::*;
use self::square::Square;
use super::types::{Data, Addr};

#[derive(Debug)]
pub struct Apu {
squares: (Square, Square),
cycle: u16,
step: usize,
sequencer_mode: bool, // t => mode 1, f => mode 0
enable_irq: bool,
}

impl Apu {
pub fn new() -> Self {
Apu {
squares: (Square::new(0), Square::new(1)),
cycle: 0,
step: 0,
sequencer_mode: false,
enable_irq: false,
}
}
pub fn run(&mut self, cycle: u16) {
self.cycle += cycle;
if self.cycle >= DIVIDE_COUNT_FOR_240HZ {
// TODO: invoked by 240hz
self.cycle -= DIVIDE_COUNT_FOR_240HZ;
if self.sequencer_mode {
self.update_by_sequence_mode1();
} else {
self.update_by_sequence_mdoe0();
}
}
}

// step 4
fn update_by_sequence_mdoe0(&mut self) {
if self.step % 2 == 1 {
self.update_counters();
}
self.step += 1;
if self.step == 4 {
if self.enable_irq {
// TODO:
}
self.step = 0;
}
self.update_envelope();
}

// step 5
fn update_by_sequence_mode1(&mut self) {
if self.step % 2 == 0 {
self.update_counters();
}
self.step += 1;
if self.step == 5 {
self.step = 0;
} else {
self.update_envelope();
}
}

// generate envelope & linear clock
fn update_envelope(&mut self) {
self.squares.0.update_envelope();
self.squares.1.update_envelope();
}

// generate length counter & sweep ckock
fn update_counters(&mut self) {
self.squares.0.update_counters();
self.squares.1.update_counters();
}

pub fn read(&mut self, addr: Addr) -> Data {
match addr {
0x15 => {
let s0 = if self.squares.0.has_count_end() {
0x00
} else {
0x01
};
let s1 = if self.squares.1.has_count_end() {
0x00
} else {
0x02
};
s1 | s0
}
_ => 0,
}
}

pub fn write(&mut self, addr: Addr, data: Data) {
match addr {
0x00..=0x03 => {
self.squares.0.write(addr, data);
}
0x04..=0x07 => {
self.squares.1.write(addr - 0x04, data);
}
0x08..=0x0b => {
}
0x0c..=0x0f => {
}
0x15 => {
if data & 0x01 == 0x01 {
self.squares.0.enable();
} else {
self.squares.0.disable();
}
if data & 0x02 == 0x02 {
self.squares.1.enable();
} else {
self.squares.1.disable();
}
}
0x17 => {
self.sequencer_mode = data & 0x80 == 0x80;
self.enable_irq = data & 0x40 != 0x40; // actually it keeps wherer irq is disabled
self.step = 0;
self.cycle = 0;
}
_ => (),
}
}
}

0 comments on commit 50f7a6e

Please sign in to comment.