Upgrade to Pro — share decks privately, control downloads, hide ads and more …

ファミコンエミュレータの創り方

bokuweb
September 07, 2018
44k

 ファミコンエミュレータの創り方

bokuweb

September 07, 2018
Tweet

Transcript

  1. $16 31" 3*$0)੡ CJU.)[ 116 ϐΫνϟʔϓϩηοαϢχοτ31$ 30. ࠷େϓϩάϥϜ30.,J# ΩϟϥΫλ30.,J# 83".

    ϫʔΩϯά3".,J# 73". ϏσΦ3".,J# ࠷େൃ৭਺ ৭ ը໘ղ૾౓ ºϐΫηϧ α΢ϯυ ۣܗ೾ɺࡾ֯೾ɺϊΠζɺ%1$. ίϯτϩʔϥ ˢɺˣɺˡɺˠɺ"ɺ#ɺ45"35ɺ4&-&$5 ϑΝϛίϯͷεϖοΫ
  2. $16 31" 116 31$ 73". ,J# 83". ,J# #6' Χηοτ

    .)[ .)[ /.* CJU116CVT CJU$16CVT "16 ϑΝϛίϯ σΟεϓϨΠ α΢ϯυ ίϯτϩʔϥ ϑΝϛίϯͷϒϩοΫਤ
  3. ------- GND -- |01 31| -- +5V CPU A11 ->

    |02 32| <- M2 CPU A10 -> |03 33| <- CPU A12 CPU A9 -> |04 34| <- CPU A13 CPU A8 -> |05 35| <- CPU A14 CPU A7 -> |06 36| <> CPU D7 CPU A6 -> |07 37| <> CPU D6 CPU A5 -> |08 38| <> CPU D5 CPU A4 -> |09 39| <> CPU D4 CPU A3 -> |10 40| <> CPU D3 CPU A2 -> |11 41| <> CPU D2 CPU A1 -> |12 42| <> CPU D1 CPU A0 -> |13 43| <> CPU D0 CPU R/W -> |14 44| <- /ROMSEL (/A15 + /M2) /IRQ <- |15 45| <- Audio from 2A03 GND -- |16 46| -> Audio to RF PPU /RD -> |17 47| <- PPU /WR CIRAM A10 <- |18 48| -> CIRAM /CE PPU A6 -> |19 49| <- PPU /A13 PPU A5 -> |20 50| <- PPU A7 PPU A4 -> |21 51| <- PPU A8 PPU A3 -> |22 52| <- PPU A9 PPU A2 -> |23 53| <- PPU A10 PPU A1 -> |24 54| <- PPU A11 PPU A0 -> |25 55| <- PPU A12 PPU D0 <> |26 56| <- PPU A13 PPU D1 <> |27 57| <> PPU D7 PPU D2 <> |28 58| <> PPU D6 PPU D3 <> |29 59| <> PPU D5 +5V -- |30 60| <> PPU D4 ------- ΧηοτͷίωΫλϐϯ഑ྻΛݟΔͱ $16116ͷCJUόε৴߸͕ͦΕͧΕ઀ଓ͞Ε͍ͯΔ Χηοτͷϐϯ഑ྻ $16ͷόε৴߸ 116ͷόε৴߸
  4. ήʔϜϓϩάϥϜ͕֨ೲ͞Εͨ30. $16͸ϓϩάϥϜ30.͔Β໋ྩϑΣονͯ͠ϓϩάϥϜ Λ࣮ߦ͢Δ void main() { char i; *(char*)0x2000 =

    0x00; *(char*)0x2001 = 0x00; ͜Μͳͷ͕ "'''"ʜ ͜Μͳײ͡Ͱ٧·ͬͯΔ ϓϩάϥϜ30.ͱ͸
  5. ̔#ZUFº#ZUFͰºαΠζͷը૾͕දݱ͞ΕΔ ҎԼ͸30.಺ͷεϓϥΠτΠϯσοΫε͕ϋʔτϚʔΫͩͬͨ৔߹ͷྫ "EESFTTT %BUB Y Y Y Y' Y Y''

    Y Y'' Y Y'' Y Y& Y Y$ Y Y "EESFTTT %BUB Y Y Y Y' Y" Y#' Y# Y#' Y$ Y'' Y% Y& Y& Y$ Y' Y ΩϟϥΫλʔ30.ͷσʔλ
  6. $POTUBOU&" /&4GPMMPXFECZ.4%04FOEPGpMF 4J[FPG13(30.JO,#VOJUT 4J[FPG$)330.JO,#VOJUT 7BMVFNFBOTUIFCPBSEVTFT$)33". 'MBHT 'MBHT 4J[FPG13(3".JO,#VOJUT

    7BMVFJOGFST,#GPSDPNQBUJCJMJUZTFF13(3".DJSDVJU 'MBHT 'MBHT VOPGpDJBM ;FSPpMMFE #ZUF ,J#ZUF #MPDLTJ[F J/&4 IFBEFS J/&4 IFBEFS ϓϩάϥϜ30. ϓϩάϥϜ30. ΩϟϥΫλʔ30. ,J#ZUF #MPDLTJ[F J/&4IFBEFS ֦ுྖҬ J/&4ϑΥʔϚοτͷத਎ #ZIFBEFSͷ#ZUFʹ ֤30.ͷϒϩοΫαΠζ͕ ֨ೲ͞Ε͍ͯΔ
  7. const run = Module.cwrap("run", null, ["number", "number"]); const res =

    await fetch("./PATH_TO_NES_FILE.nes"); const arrayBuf = await res.arrayBuffer(); const nes = new Uint8Array(arrayBuf); const size = nes.byteLength; const ptr = Module._malloc(size); const buf = new Uint8Array(Module.HEAPU8.buffer, ptr, size); buf.set(nes); run(size, buf.byteOffset); +BWB4DSJQUଆͷॳظԽॲཧ 3VTUଆͷؔ਺Λ+4 ͔Βݺ΂ΔΑ͏ʹ ͜Ε͸Ҿ਺
  8. const run = Module.cwrap("run", null, ["number", "number"]); const res =

    await fetch("./PATH_TO_NES_FILE.nes"); const arrayBuf = await res.arrayBuffer(); const nes = new Uint8Array(arrayBuf); const size = nes.byteLength; const ptr = Module._malloc(size); const buf = new Uint8Array(Module.HEAPU8.buffer, ptr, size); buf.set(nes); run(size, buf.byteOffset); +BWB4DSJQUଆͷॳظԽॲཧ OFTϑΝΠϧΛϑΣον ͯ͠"SSBZ#VGGFSʹม׵
  9. const run = Module.cwrap("run", null, ["number", "number"]); const res =

    await fetch("./PATH_TO_NES_FILE.nes"); const arrayBuf = await res.arrayBuffer(); const nes = new Uint8Array(arrayBuf); const size = nes.byteLength; const ptr = Module._malloc(size); const buf = new Uint8Array(Module.HEAPU8.buffer, ptr, size); buf.set(nes); run(size, buf.byteOffset); +BWB4DSJQUଆͷॳظԽॲཧ OFTϑΝΠϧ෼ͷྖҬΛ NBMMPDͰ֬อ ʢFNTDSJQUFOΛ࢖༻ͨ͠৔߹ .PEVMFʹੜ͑ͯΔʣ +4ଆ͔Β͸XBTNଆͷϝϞϦ͸"SSBZ#VGGFS ʹݟ͑ΔͷͰCZUF0GGTFUΛJ/&4σʔλͷઌ಄ ʢ͢ͳΘͪϙΠϯλʣͱͯ͠αΠζͱҰॹʹڭ͑ ͯ΍Δ
  10. #[no_mangle] pub fn run(len: usize, ptr: *mut u8) { let

    buf: &mut [u8] = unsafe { std::slice::from_raw_parts_mut(ptr, len + 1) }; let cassette = parse(buf); let prom = Rom::new(cassette.program_rom); let crom = Rom::new(cassette.character_rom); // ... লུ …. } pub fn parse(buf: &mut [u8]) -> Cassette { let program_rom_pages = buf[4] as usize; let character_rom_pages = buf[5] as usize; let character_rom_start = NES_HEADER_SIZE + program_rom_pages * PROGRAM_ROM_SIZE; let character_rom_end = character_rom_start + character_rom_pages * CHARACTER_ROM_SIZE; Cassette { program_rom: buf[NES_HEADER_SIZE..character_rom_start].to_vec(), character_ram: buf[character_rom_start..character_rom_end].to_vec(), } } 3VTU XBTN ଆͷॳظԽॲཧ +4ଆ͔ΒJ/&4σʔλͷ αΠζͱϙΠϯλΛ΋Β͏ QBSTF֤ͯ͠30.Λ෼཭ ޙड़͢Δ$16͕ϓϩάϥϜ30. ͷ಺༰Λ࣮ߦ͢Ε͹ήʔϜ͕։࢝͢Δʣ
  11. #[no_mangle] pub fn run(len: usize, ptr: *mut u8) { let

    buf: &mut [u8] = unsafe { std::slice::from_raw_parts_mut(ptr, len + 1) }; let cassette = parse(buf); let prom = Rom::new(cassette.program_rom); let crom = Rom::new(cassette.character_rom); // ... লུ …. } pub fn parse(buf: &mut [u8]) -> Cassette { let program_rom_pages = buf[4] as usize; let character_rom_pages = buf[5] as usize; let character_rom_start = NES_HEADER_SIZE + program_rom_pages * PROGRAM_ROM_SIZE; let character_rom_end = character_rom_start + character_rom_pages * CHARACTER_ROM_SIZE; Cassette { program_rom: buf[NES_HEADER_SIZE..character_rom_start].to_vec(), character_ram: buf[character_rom_start..character_rom_end].to_vec(), } } 3VTU XBTN ଆͷॳظԽॲཧ J/&4ϔομͷ৘ใ͔Β ֤30.Λ੾Γग़͢
  12. build: mkdir -p wasm rm -rf target/wasm32-unknown-emscripten/release/deps/*.wasm rm -rf target/wasm32-unknown-emscripten/release/rustynes.js

    cargo rustc --release \ --target=wasm32-unknown-emscripten -- \ -C opt-level=3 \ -C link-args="-O3 -s -s EXPORTED_FUNCTIONS=['_run'] -s EXTRA_EXPORTED_RUNTIME_METHODS=['cwrap']" \ --verbose cp target/wasm32-unknown-emscripten/release/rustynes.js wasm/rustynes.js cp target/wasm32-unknown-emscripten/release/deps/*.wasm wasm/rustynes.wasm wasm-gc wasm/rustynes.wasm wasm/rustynes.wasm ϏϧυίϚϯυ +4ଆʹFYQPSU͢Δؔ਺Λ ࢦఆͯ͠΍Δඞཁ͕͋Δ
  13. pub struct Ram { pub field: Vec<u8>, } impl Ram

    { pub fn new(buf: Vec<u8>) -> Ram { Ram { field: buf } } pub fn read(&self, addr: u16) -> u8 { self.field[addr as usize] } pub fn write(&mut self, addr: u16, data: u8) { self.field[addr as usize] = data; } } 3".ͷ࣮૷
  14. ΞυϨε νοϓηϨΫτ Ϧʔυ σʔλ Y%&"% Y#& 43".ͷY%&"%൪஍ʹϦʔυΞΫηεΛߦ͍Y#&͕ಡΈग़͞ΕΔྫ 43".ͷϦʔυλΠϛϯάྫ ͜ͷλΠϛϯάͰY#&Λϥον͢Δ σʔλ͸Ϧʔυ৴߸ͷ্ཱ͕ͪΓ΍ͦͷޙͷ

    $16ΫϩοΫͷ্ཱ͕ͪΓͰऔΓࠐ·ΕΔ΋ͷ͕ଟ͍ σόΠεʹϓϦϯτ͞Ε͍ͯΔ਺ࣈ͸͍͍ͩͨ͜͜ͷ਺ࣈ ʢDZDMF खݩͷ30.͸ͳͷͰ DZDMFOTҎ্ʹͯ͠Ͷͱ͍͏ҙຯ ׂ Ѫ
  15. $16

  16. $16ͷϨδελҰཡ ໊শ αΠζ ৄࡉ " CJU ΞΩϡϜϨʔλ 9 CJU ΠϯσοΫεϨδελ

    : CJU ΠϯσοΫεϨδελ 4 CJU ελοΫϙΠϯλ 1 CJU εςʔλεϨδελ 1$ CJU ϓϩάϥϜΧ΢ϯλ ελοΫϙΠϯλ͸ϏοτͷΞυϨεۭؒΛࢦ͢ඞཁ͕͋Δ͕ ্ҐCJU͸Yʹݻఆ͞Ε͍ͯΔ ʢ83".ͷ͏ͪYʙY''ׂ͕Γ౰ͯΒΕΔʣ ͢ͳΘͪɺελοΫϙΠϯλϨδελ͕Y"ͷ৔߹ɺ ελοΫϙΠϯλ͸Y"ʹͳΔ ԋࢉ͸"ϨδελͰߦΘΕɺ9 :Ϩδελ͸ΠϯσοΫεʹ࢖༻͞ΕΔ

  17. εςʔλεϨδελ CJU ໊শ ৄࡉ ಺༰ CJU / ωΨςΟϒ ԋࢉ݁ՌͷCJU͕ͷ࣌ʹηοτ CJU

    7 Φʔόʔϑϩʔ 1ԋࢉ݁Ռ͕ΦʔόʔϑϩʔΛىͨ࣌͜͠ʹηο τ CJU 3 ༧໿ࡁΈ ৗʹηοτ͞Ε͍ͯΔ CJU # ϒϨʔΫϞʔυ #3,ൃੜ࣌ʹηοτɺ*32ൃੜ࣌ʹΫϦΞ CJU % σγϚϧϞʔυ σϑΥϧτɺ#$%Ϟʔυ ະ࣮૷ CJU * *32ېࢭ *32ڐՄɺ*32ېࢭ CJU ; θϩ ԋࢉ݁Ռ͕ͷ࣌ʹηοτ CJU $ ΩϟϦʔ ΩϟϦʔൃੜ࣌ʹηοτ εςʔλεϨδελͷৄࡉ CJU͸ৗʹͰɺCJU͸/&4Ͱ͸ະ࣮૷
 *32͸ׂΓࠐΈɺ#3,͸ιϑτ΢ΤΞׂΓࠐΈ
  18. struct Status { negative: bool, overflow: bool, reserved: bool, break_mode:

    bool, decimal_mode: bool, interrupt: bool, zero: bool, carry: bool, } #[allow(non_snake_case)] pub struct Registers { A: u8, X: u8, Y: u8, SP: u8, PC: u16, P: Status, } $16ͷϨδελͷ࣮૷
  19. ΞυϨε αΠζ ༻్ YʙY'' Y 83". YʙY''' 83".ͷϛϥʔ YʙY

    Y 116Ϩδελ YʙY''' 116Ϩδελͷϛϥʔ YʙY' Y "16*0ɺίϯτϩʔϥɺ%." YʙY''' Y'& ֦ு30. YʙY''' Y ֦ு3". YʙY#''' Y ϓϩάϥϜ30. Y$ʙY'''' Y ϓϩάϥϜ30. $16ͷϝϞϦϚοϓ Χηοτͷ ϓϩάϥϜ30.
  20. impl<'a> CpuBus for Bus<'a> { fn read_word(&mut self, addr: u16)

    -> u16 { // …লུ… } fn read(&mut self, addr: u16) -> u8 { match addr { 0x0000...0x07FF => self.work_ram.read(addr), 0x0800...0x1FFF => self.work_ram.read(addr - 0x0800), 0x2000...0x3FFF => self.ppu.read(addr - 0x2000), 0x4016 => self.keypad.read(), 0x4017 => unimplemented!(), // 2player controller 0x4000...0x401F => self.apu.read(addr - 0x4000), 0x6000...0x7FFF => unimplemented!(), // battery back up ram 0x8000...0xBFFF => self.program_rom.read(addr - 0x8000), 0xC000...0xFFFF if self.program_rom.size() <= 0x4000 => self.program_rom.read(addr - 0xC000) 0xC000...0xFFFF => self.program_rom.read(addr - 0x8000), _ => panic!("[READ] There is an illegal address. (0x{:x}) access detected.", addr), } } fn write(&mut self, addr: u16, data: u8) { // …লུ… } } $16͸$QV#VTϞδϡʔϧΛհ֤ͯ͠σόΠεʹΞΫηε͢ΔΑ͏ʹ $16όεͷ࣮૷ ϝϞϦϚοϓΛ൓ө
  21. ͨͱ͑͹ɺϦηοτʢϑΝϛίϯͷ࢛֯ͷϘλϯͰ͢Ͷʣ΋ׂΓࠐΈͷҰछͰ Ϧηοτ͕ൃੜͨ͠৔߹Y'''$ɺY'''%͔ΒCJUͷΞυϨεΛϦʔυ͠ 1$ʢϓϩάϥϜΧ΢ϯλʣʹηοτ͠ɺηοτ͞ΕͨΞυϨε͔Β࣮ߦ͠·͢ ׂΓࠐΈ ԼҐόΠτ ্ҐόΠτ ֓ཁ /.* Y'''" Y'''#

    ϊϯϚεΧϥϒϧׂΓࠐΈͱݴͬͯ$16͸ଆͰϚεΫͰ͖ͳ ׂ͍ΓࠐΈͰ͢ɻ116ͷׂΓࠐΈग़ྗ৴߸͕઀ଓ͞Ε͍ͯ· ͢ 3&4&5 Y'''$ Y'''% ϦηοτϘλϯԡԼ࣌΍ిݯ౤ೖ࣌ʹ͔͔ΔׂΓࠐΈͰ͢ *32ɺ#3, Y'''& Y'''' #3,͸ιϑτ΢ΣΞׂΓࠐΈͰ#3,໋ྩΛ࣮ߦͨ͠ͱ͖ʹൃ ੜ͠·͢ɻ*32͸"16΍Χηοτʹ઀ଓ͞Ε͍ͯ·͢ ͷ৔߹ҎԼͷΑ͏ͳׂΓࠐΈཁҼ͕༻ҙ͞Ε͍ͯ·͢ ͷׂΓࠐΈ
  22. pub fn reset<T: CpuRegisters, U: CpuBus>(registers: &mut T, bus: &mut

    U) { let pc = bus.read_word(0xFFFC); registers.set_PC(pc); } جຊతʹ͸ϓϩάϥϜ30.͸Yʹ഑ஔ͞Ε͍ͯΔͷͰ Y'''$͔ΒYɺY'''%͔ΒY͕Ϧʔυ͞ΕΔέʔε͕ଟ͍ ͦ͏͢Δͱ࣍αΠΫϧ͸ϓϩάϥϜ30.ͷઌ಄Ͱ͋ΔY͔Β࣮ߦ͞ΕΔ ϦηοτׂΓࠐΈͷ࣮૷ Y'''$Λ্ҐόΠτ Y'''%ΛԼҐόΠτ ͱͨ͠CJUΛϦʔυ Ϧʔυ͞ΕͨCJUͷΞυϨεΛ 1$ʢϓϩάϥϜΧ΢ϯλʣʹηοτ ࣍αΠΫϧ͸ηοτͨ͠ΞυϨε͔Β࣮ߦ͞ΕΔ
  23. pub fn process_nmi<T: CpuRegisters, U: CpuBus>(registers: &mut T, bus: &mut

    U) { registers.set_break(false); push((registers.get_PC() >> 8) as u8, registers, bus); push(registers.get_PC() as u8, registers, bus); push_status(registers, bus); registers.set_interrupt(true); let next = bus.read_word(0xFFFA); registers.set_PC(next); } /.*ͷ࣮૷ 1$ͷ্ҐCJUɺ1$ͷԼҐCJU ͷॱʹελοΫʹ164)
  24. pub fn process_nmi<T: CpuRegisters, U: CpuBus>(registers: &mut T, bus: &mut

    U) { registers.set_break(false); push((registers.get_PC() >> 8) as u8, registers, bus); push(registers.get_PC() as u8, registers, bus); push_status(registers, bus); registers.set_interrupt(true); let next = bus.read_word(0xFFFA); registers.set_PC(next); } /.*ͷ࣮૷ εςʔλεϨδελΛ ελοΫʹ164)
  25. pub fn process_nmi<T: CpuRegisters, U: CpuBus>(registers: &mut T, bus: &mut

    U) { registers.set_break(false); push((registers.get_PC() >> 8) as u8, registers, bus); push(registers.get_PC() as u8, registers, bus); push_status(registers, bus); registers.set_interrupt(true); let next = bus.read_word(0xFFFA); registers.set_PC(next); } /.*ͷ࣮૷ Y'''" Y'''#͔ΒΞυϨεΛ Ϧʔυ͠1$ʹηοτ
  26. ྫ͑͹/.*લͷ1$͕YɺεςʔλεϨδελ͕Y"" ελοΫϙΠϯλ͕Y''ͩͱ͢Δͱ/.*ޙͷελοΫ͸ҎԼͷΠϝʔδ Y'' Y'&' Y'&& Y'&% Y'& ελοΫϙΠϯλ ʜ Y

    Y Y"" 1$ͷ্ҐΞυϨε 1$ͷԼҐΞυϨε εςʔλεϨδελ ࣍ճͷ164)͸͜͜ʹੵ·ΕΔ /.*ͷ࣮ߦޙͷελοΫΠϝʔδ
  27. ུশ ໊લ ֓ཁ JNQM *NQMJFE ϨδελΛૢ࡞͢Δ " "DDVNVMBUPS "ϨδελΛૢ࡞͢Δ

    *NNFEJBUF Φϖίʔυ͕֨ೲ͞Ε͍ͯͨ࣍ͷ൪஍ʹ֨ೲ͞Ε͍ͯΔ஋Λσʔλͱͯ͠ѻ͏ [QH ;FSPQBHF YΛ্ҐΞυϨεɺ1$ʢΦϖίʔυͷ࣍ͷ൪஍ʣʹ֨ೲ͞Εͨ஋ΛԼҐΞυϨεͱͨ͠൪஍Λԋ ࢉର৅ͱ͢Δ [QH 9 [QH : ;FSPQBHF JOEFYFE YΛ্ҐΞυϨεɺ1$ʢΦϖίʔυͷ࣍ͷ൪஍ʣʹ֨ೲ͞Εͨ஋ʹ9Ϩδελ·ͨ͸:Ϩδελ ΛՃࢉͨ͠஋ΛԼҐΞυϨεͱͨ͠൪஍Λԋࢉର৅ͱ͢Δ BCT "CTPMVUF 1$ʢΦϖίʔυͷ࣍ͷ൪஍ʣʹ֨ೲ͞Εͨ஋ΛԼҐΞυϨεɺ1$ʢΦϖίʔυͷ࣍ͷ࣍ͷ൪஍ʣʹ ֨ೲ͞Εͨ஋Λ্ҐΞυϨεͱͨ͠൪஍Λԋࢉର৅ͱ͢Δ BCT 9 BCT : "CTPMVUF JOEFYFE "CTPMVUF"EESFTTJOHͰಘΒΕΔ஋ʹ9Ϩδελ·ͨ͸:ϨδελΛՃࢉͨ͠൪஍Λԋࢉର৅ͱ͢Δ SFM 3FMBUJWF 1$ʢΦϖίʔυͷ࣍ͷ൪஍ʣʹ֨ೲ͞Εͨ஋ͱͦͷ࣍ͷ൪஍ΛՃࢉͨ͠൪஍Λԋࢉର৅ͱ͢Δ 9 *OE *OEFYFE *OEJSFDU YΛ্ҐΞυϨεɺ1$ʢΦϖίʔυͷ࣍ͷ൪஍ʣʹ֨ೲ͞Εͨ஋ΛԼҐΞυϨεͱͨ͠൪஍ʹϨ δελ9ͷ஋ΛՃࢉɺͦͷ൪஍ͷ஋ΛԼҐΞυϨεɺͦͷ࣍ͷ൪஍ͷ஋Λ্ҐΞυϨεͱͨ͠൪஍Λ ԋࢉର৅ͱ͢Δ *OE : *OEJSFDU JOEFYFE YΛ্ҐΞυϨεɺ1$ʢΦϖίʔυͷ࣍ͷ൪஍ʣʹ֨ೲ͞Εͨ஋ΛԼҐΞυϨεͱͨ͠൪஍ͷ஋ ΛԼҐΞυϨεɺͦͷ࣍ͷ൪஍ͷ஋Λ্ҐΞυϨεͱͨ͠൪஍ʹϨδελ:ΛՃࢉͨ͠൪஍Λԋࢉର ৅ͱ͢Δ *OE "CTPMVUF JOEFYFE "CTPMVUF"EESFTTJOHͰಘΒΕΔ൪஍ʹ֨ೲ͞Ε͍ͯΔ஋ΛԼҐΞυϨεɺͦͷ࣍ͷ൪஍ʹ֨ೲ͞ Ε͍ͯΔ஋Λ্ҐΞυϨεͱͨ͠൪஍Λԋࢉର৅ͱ͢Δ ΞυϨογϯάϞʔυ
  28. Yʹͱ͍͏໋ྩ͕֨ೲ͞Ε͍ͯΔ৔߹ ϝϞϦͱ࣮ߦखॱҎԼͷΑ͏ʹͳΔʢ1$΋Yͱ͢Δʣ -%" ʢY"ʣ Y Y" Y ʜ Y -%""

    $16͸1$ ͢ͳΘͪY ͔Β໋ྩΛϑΣον͠1$Λ ϑΣον݁ՌY"͔Β-%"JNNͰ͋Δͱ൑ผ͞ΕΔ $16͸Y͔ΒΦϖϥϯυY"ΛϑΣον͠1$Λ Ϩδελ"ʹY"Λ֨ೲ͠ɺ࣍ͷ໋ྩ΁ -%"JNN ࣍ͷ໋ྩ ͜ͷ஋Λ"Ϩδελ ʹϩʔυ͢Δ 1$
  29. pub fn lda_imm<T: CpuRegisters>(operand: Word, registers: &mut T) { registers

    .set_A(operand as Data) .update_negative_by(operand as Data) .update_zero_by(operand as Data); } #[test] fn test_lda_immediate() { let mut reg = Registers::new(); lda_imm(0xA5, &mut reg); assert_eq!(reg.get_A(), 0xA5); } ଈ஋Λ"Ϩδελʹϩʔυ͢Δ -%"JNN "ϨδελʹΦϖϥϯυ ʹηοτ ηοτ͞Εͨ஋ʹରԠ͢Δ εςʔλεϑϥάΛηοτ
  30. Yʹͱ͍͏໋ྩ͕֨ೲɺYʹ Y"͕֨ೲ͞Ε͍ͯΔ৔߹ϝϞϦͱ࣮ߦखॱҎԼͷΑ͏ʹͳΔ ʢ1$΋Yͱ͢Δʣ -%" ʢY"ʣ Y Y Y ʜ Y

    -%" $16͸1$ ͢ͳΘͪY ͔Β໋ྩΛϑΣον͠1$Λ ϑΣον݁ՌY"͔Β-%"[QHͰ͋Δͱ൑ผ͞ΕΔ $16͸Y͔ΒΦϖϥϯυYΛϑΣον͠1$Λ $16͸Y͔ΒY"ΛϦʔυ Ϩδελ"ʹY"Λ֨ೲ͠ɺ࣍ͷ໋ྩ΁ Y" Y ʜ Y ʜ Y ᶃY൪஍ͷ಺༰ΛϦʔυ ᶄY"Λ"Ϩδελʹηοτ -%"[QH ࣍ͷ໋ྩ 1$
  31. pub fn lda<T: CpuRegisters, U: CpuBus>(operand: Word, registers: &mut T,

    bus: &mut U) { let computed = bus.read(operand); registers .set_A(computed) .update_negative_by(computed) .update_zero_by(computed); } #[test] fn test_lda() { let mut reg = Registers::new(); let mut bus = MockBus::new(); bus.mem[0xAA] = 0xA5; lda(0xAA, &mut reg, &mut bus); assert_eq!(reg.get_A(), 0xA5); } -%"[QH ্ҐΛYԼҐΛΦϖϥϯυͱͨ͠ΞυϨεʹ ֨ೲ͞Ε͍ͯΔ஋Λ"Ϩδελʹϩʔυ͢Δϩʔυ͢Δ Φϖϥϯυͷ஋ΞυϨεͱͯ͠Ϧʔυ Ϧʔυ͞Εͨ஋Λ"Ϩδελʹηοτ
  32. Ϧηοτ ແ͠ ແ͠ ༗Γ ༗Γ ׂΓࠐΈ ༗ແ Φϖϥϯυ ༗ແ Ϩδελͷ

    ঢ়ଶΛୀආ 1$͔Β໋ྩ ΛϑΣον 1$ 1$͔ΒΦϖϥϯυΛ ϑΣον 1$ ໋ྩΛ࣮ߦ ׂΓࠐΈϕΫλ ͷ஋Λ1$ ʹηοτ $16ͷಈ࡞Πϝʔδ Y'''$'''%ʹ ֨ೲ͞Εͨ൪஍͔Β։࢝
  33. pub fn run<T: CpuRegisters + Debug, U: CpuBus>( registers: &mut

    T, bus: &mut U, nmi: &mut bool, ) -> Data { if *nmi { process_nmi(registers, bus); *nmi = false; } let code = fetch(registers, bus); let ref map = opecode::MAP; let code = &*map.get(&code).unwrap(); let operand = fetch_operand(&code, registers, bus); match code.name { Instruction::LDA if code.mode == Addressing::Immediate => lda_imm(operand, registers), Instruction::LDA => lda(operand, registers, bus), // …লུ… } code.cycle } $16ͷ࣮૷ ׂΓࠐΈͷνΣοΫ
  34. pub fn run<T: CpuRegisters + Debug, U: CpuBus>( registers: &mut

    T, bus: &mut U, nmi: &mut bool, ) -> Data { if *nmi { process_nmi(registers, bus); *nmi = false; } let code = fetch(registers, bus); let ref map = opecode::MAP; let code = &*map.get(&code).unwrap(); let operand = fetch_operand(&code, registers, bus); match code.name { Instruction::LDA if code.mode == Addressing::Immediate => lda_imm(operand, registers), Instruction::LDA => lda(operand, registers, bus), // …লུ… } code.cycle } $16ͷ࣮૷ 1$͔Β໋ྩΛϑΣον σίʔυ ࣙॻ͔Β໋ྩͷछผɾΞυϨογϯά ϞʔυɺαΠΫϧ਺ͳͲΛҾ͍ͯ͘Δ
  35. $16ͷ࣮૷ let mut m = HashMap::new(); m.insert(0xA9, Opecode { name:

    Instruction::LDA, mode: Addressing::Immediate, cycle: cycles[0xA9] }); m.insert(0xA5, Opecode { name: Instruction::LDA, mode: Addressing::ZeroPage, cycle: cycles[0xA5] }); m.insert(0xB5, Opecode { name: Instruction::LDA, mode: Addressing::ZeroPageX, cycle: cycles[0xB5] }); m.insert(0xAD, Opecode { name: Instruction::LDA, mode: Addressing::Absolute, cycle: cycles[0xAD] }); m.insert(0xBD, Opecode { name: Instruction::LDA, mode: Addressing::AbsoluteX, cycle: cycles[0xBD] }); m.insert(0xB9, Opecode { name: Instruction::LDA, mode: Addressing::AbsoluteY, cycle: cycles[0xB9] }); m.insert(0xA2, Opecode { name: Instruction::LDX, mode: Addressing::Immediate, cycle: cycles[0xA2] }); m.insert(0xA6, Opecode { name: Instruction::LDX, mode: Addressing::ZeroPage, cycle: cycles[0xA6] }); // …লུ… ΞυϨογϯάϞʔυ΍αΠΫϧ਺ Λ൑ผ
  36. pub fn run<T: CpuRegisters + Debug, U: CpuBus>( registers: &mut

    T, bus: &mut U, nmi: &mut bool, ) -> Data { if *nmi { process_nmi(registers, bus); *nmi = false; } let code = fetch(registers, bus); let ref map = opecode::MAP; let code = &*map.get(&code).unwrap(); let operand = fetch_operand(&code, registers, bus); match code.name { Instruction::LDA if code.mode == Addressing::Immediate => lda_imm(operand, registers), Instruction::LDA => lda(operand, registers, bus), // …লུ… } code.cycle } $16ͷ࣮૷ 1$͔ΒΦϖϥϯυΛϑΣον αΠΫϧ਺Λฦ͢ -%"JNNҎ֎ͷ-%"໋ྩ -%"JNN໋ྩ
  37. 116

  38. ΞυϨε αΠζ ֓ཁ YʙY''' Y ύλʔϯςʔϒϧ YʙY''' Y ύλʔϯςʔϒϧ YʙY#'

    Y$ ωʔϜςʔϒϧ Y$ʙY'' Y ଐੑςʔϒϧ YʙY#' Y$ ωʔϜςʔϒϧ Y$ʙY'' Y ଐੑςʔϒϧ YʙY##' Y$ ωʔϜςʔϒϧ Y#$ʙY#'' Y ଐੑςʔϒϧ Y$ʙY'#' Y$ ωʔϜςʔϒϧ Y'$ʙY''' Y ଐੑςʔϒϧ YʙY&'' YY&''ͷϛϥʔ Y'ʙY'' Y όοΫάϥ΢ϯυύϨοτςʔϒϧ Y'ʙY'' Y εϓϥΠτύϨοτςʔϒϧ Y'ʙY''' Y'Y''ͷϛϥʔ 116ͷϝϞϦϚοϓ Χηοτͷ ΩϟϥΫλʔ30.
  39. ͨͱ͑͹εύʔϚϦΦϒϥβʔεͷΦʔϓχϯάը໘͸ҎԼͷΑ͏ͳ ύϨοτ͕ઃఆ͞Ε͍ͯΔ ύϨοτY' ύϨοτY' ύϨοτY' ύϨοτY'$ ύϨοτY' ύϨοτY' ύϨοτY' ύϨοτY'$

    എܠ༻ύϨοτʢഎܠ৭৭ ৭YύϨοτͰ৭ʣ εϓϥΠτ༻ύϨοτʢ৭͸ಁ໌৭ͳͷͰ৭YύϨοτͰ৭ʣ ֤ςʔϒϧͷઌ಄ ͸ಁ໌৭ͱͯ͠ ѻΘΕΔ ಁ໌৭ ಁ໌৭ ಁ໌৭ ֤ςʔϒϧͷઌ಄ ͸എܠ৭ എܠ৭ എܠ৭ എܠ৭ ϚϦΦ͸ӈଆͷ৭Ͱදݱ͞Ε͍ͯΔ ύϨοτςʔϒϧͱ͸
  40. ΞυϨε ֓ཁ Y Y CJU આ໌ 7#MBOL࣌ʹׂΓࠐΈΛग़ྗ ৗʹ̍

    εϓϥΠταΠζYY എܠ༻ύλʔϯςʔϒϧΞυϨεYY εϓϥΠτύλʔϯςʔϒϧΞυϨεYY 116ΞυϨεΠϯΫϦϝϯτ ωʔϜςʔϒϧࢦఆ CJU આ໌ എܠ৭CࠇC྘C੨C੺
 εϓϥΠτΠωʔϒϧσΟηʔϒϧΠωʔϒϧ എܠΠωʔϒϧσΟηʔϒϧΠωʔϒϧ εϓϥΠτͷը໘ࠨϐΫηϧඳըඳը͠ͳ͍ඳը͢Δ എܠͷը໘ࠨϐΫηϧඳըඳը͠ͳ͍ඳը͢Δ σΟεϓϨΠλΠϓΧϥʔϞϊΫϩ 116ͷϨδελ 8SJUF
  41. ΞυϨε ֓ཁ Y εϓϥΠτϝϞϦΞυϨε YΛܦ༝ͯ͠εϓϥΠτϝϞϦ΁ॻ͖ࠐΉϏοτΞυϨεΛࢦఆ Y εϓϥΠτϝϞϦσʔλ YʹΑͬͯࢦఆ͞ΕͨεϓϥΠτϝϞϦΞυϨε΁σʔλΛॻ͖ࠐΉɻ ॻ͖ࠐΉ౓ʹεϓϥΠτϝϞϦΞυϨε͸ΠϯΫϦϝϯτ

    ͞ΕΔɻ Y എܠεΫϩʔϧΦϑηοτ ճϥΠτΛߦ͏͜ͱͰ9ɺ:ͷॱʹεΫϩʔϧ஋͕ઃఆ͞ΕΔ Y 116"%%3 116ϝϞϦΞυϨε YΛܦ༝ͯ͠116ϝϞϦ΁ॻ͖ࠐΉϏοτΞυϨεΛࢦఆ͢Δɻ্ҐϏοτɺԼҐϏοτͷॱ ʹॻ͖ࠐΉɻ Y 116%"5" 116ϝϞϦσʔλ YʹΑͬͯࢦఆ͞Εͨ116ϝϞϦΞυϨε΁σʔλΛॻ͖ࠐΉɻॻ͖ࠐΉ౓ʹϝϞϦΞυϨε͸Π ϯΫϦϝϯτ YͷϏοτʹΑͬͯ ɺ ͢Δɻ 116ͷϨδελ 8SJUF
  42. ΞυϨε ֓ཁ Y Y εϓϥΠτϝϞϦσʔλ YʹΑͬͯࢦఆ͞ΕͨεϓϥΠτϝϞϦΞυϨε΁σʔλΛϦʔυ͢Δ Ϧʔυ͢Δ౓ʹεϓϥΠτϝϞϦΞυϨε͸ΠϯΫϦϝϯτ ͞ΕΔɻ Y

    116ϝϞϦσʔλ YʹΑͬͯࢦఆ͞Εͨ116ϝϞϦΞυϨεͷσʔλΛϦʔυ͢Δ Ϧʔυ͢Δ౓ʹϝϞϦΞυϨε͸ΠϯΫϦϝϯτ YͷϏοτʹΑͬͯ ɺ ͢Δɻ CJU આ໌ 7CMBOL࣌ʹ εϓϥΠτώοτ࣌ʹ εΩϟϯϥΠϯεϓϥΠτ਺ɹݸҎԼݸҎ্ 3FTFSWFE 116ͷϨδελ 3FBE
  43. )FMMP 8PSMEͷ)ΛωʔϜςʔϒϧ͔ΒϐΫηϧσʔλʹͯ͠ΈΔྫ ωʔϜςʔϒϧͷΛϦʔυ͠εϓϥΠτ൪߸ΛಘΔ εϓϥΠτ͸ΩϟϥΫλʔ30.಺ʹ#ZUFͣͭฒΜͰ͍ΔͷͰ YYY͔Β#ZUFϦʔυ͠εϓϥΠτΛ૊ΈཱͯΔ Y$൪஍

    Y ֘౰ͷ࠲ඪ͸ଐੑςʔϒϧͷΞυϨεY%"ʹ֘౰͢ΔͨΊ 73".ͷ͔ΒΞτϦϏϡʔτ͕Ϧʔυ͞ΕΔ Y%"൪஍ Y 116ඳըखॱΠϝʔδ ͜ΜͳεϓϥΠτ σʔλ͕ಘΒΕΔ
  44. impl Tile { pub fn new<P: PaletteRam>( vram: &Ram, cram:

    &Ram, palette: &P, position: &SpritePosition, config: &SpriteConfig, ) -> Self { let block_id = get_block_id(position); let sprite_id = get_sprite_id(&vram, position, config); let attr = get_attribute(&vram, position, config); let palette_id = (attr >> (block_id * 2)) & 0x03; let sprite = build(&cram, sprite_id, config.offset_addr_by_background_table); Tile { sprite, palette: palette.get(palette_id, PaletteType::Background) } } } pub fn build(cram: &Ram, sprite_id: u8, offset: u16) -> Sprite { let mut sprite: Sprite = (0..8).into_iter().map(|_| vec![0; 8]).collect(); for i in 0..16 { for j in 0..8 { let addr = (sprite_id as u16) * 16 + i + offset; let ram = cram.read(addr); if ram & (0x80 >> j) as u8 != 0 { sprite[(i % 8) as usize][j] += (0x01 << (i / 8)) as u8; } } } sprite } 116ඳըखॱΠϝʔδ ࠲ඪ͔ΒϒϩοΫ൪߸ Λࢉग़ ࠲ඪͱωʔϜςʔϒϧ͔Β εϓϥΠτ൪߸Λࢉग़
  45. impl Tile { pub fn new<P: PaletteRam>( vram: &Ram, cram:

    &Ram, palette: &P, position: &SpritePosition, config: &SpriteConfig, ) -> Self { let block_id = get_block_id(position); let sprite_id = get_sprite_id(&vram, position, config); let attr = get_attribute(&vram, position, config); let palette_id = (attr >> (block_id * 2)) & 0x03; let sprite = build(&cram, sprite_id, config.offset_addr_by_background_table); Tile { sprite, palette: palette.get(palette_id, PaletteType::Background) } } } pub fn build(cram: &Ram, sprite_id: u8, offset: u16) -> Sprite { let mut sprite: Sprite = (0..8).into_iter().map(|_| vec![0; 8]).collect(); for i in 0..16 { for j in 0..8 { let addr = (sprite_id as u16) * 16 + i + offset; let ram = cram.read(addr); if ram & (0x80 >> j) as u8 != 0 { sprite[(i % 8) as usize][j] += (0x01 << (i / 8)) as u8; } } } sprite } 116ඳըखॱΠϝʔδ ࠲ඪͱଐੑςʔϒϧ͔Β ΞτϦϏϡʔτΛࢉग़ ΞτϦϏϡʔτͱϒϩοΫ൪߸͔Β ύϨοτ൪߸Λࢉग़
  46. impl Tile { pub fn new<P: PaletteRam>( vram: &Ram, cram:

    &Ram, palette: &P, position: &SpritePosition, config: &SpriteConfig, ) -> Self { let block_id = get_block_id(position); let sprite_id = get_sprite_id(&vram, position, config); let attr = get_attribute(&vram, position, config); let palette_id = (attr >> (block_id * 2)) & 0x03; let sprite = build(&cram, sprite_id, config.offset_addr_by_background_table); Tile { sprite, palette: palette.get(palette_id, PaletteType::Background) } } } pub fn build(cram: &Ram, sprite_id: u8, offset: u16) -> Sprite { let mut sprite: Sprite = (0..8).into_iter().map(|_| vec![0; 8]).collect(); for i in 0..16 { for j in 0..8 { let addr = (sprite_id as u16) * 16 + i + offset; let ram = cram.read(addr); if ram & (0x80 >> j) as u8 != 0 { sprite[(i % 8) as usize][j] += (0x01 << (i / 8)) as u8; } } } sprite } 116ඳըखॱΠϝʔδ εϓϥΠτ൪߸͔Β YͷϐΫηϧσʔλΛ ࡞੒
  47. fn render_tile(&mut self, bg: &BackgroundCtx, x: usize, y: usize) {

    let offset_x = (bg.scroll_x % 8) as i32; let offset_y = (bg.scroll_y % 8) as i32; for i in 0..8 { for j in 0..8 { let x = (x + j) as i32 - offset_x; let y = (y + i) as i32 - offset_y; if x >= 0 as i32 && 0xFF >= x && y >= 0 as i32 && y < 224 { let color_id = bg.tile.palette[bg.tile.sprite[i][j] as usize]; let color = COLORS[color_id as usize]; let index = ((x + (y * 0x100)) * 4) as usize; self.buf[index] = color.0; self.buf[index + 1] = color.1; self.buf[index + 2] = color.2; if x < 8 { self.buf[index + 3] = 0; } } } } } pub static COLORS: &'static [(u8, u8, u8)] = &[ (0x80, 0x80, 0x80), (0x00, 0x3D, 0xA6), (0x00, 0x12, 0xB0), (0x44, 0x00, 0x96), …লུ… ]; 116ඳըखॱΠϝʔδ ً౓ɺ৭౓ͷσʔλΛ 3(#ʹม׵͢Δςʔϒϧ εϓϥΠτɺύϨοτ ͔Β3(#"ͷϐΫηϧσʔλ ʹม׵
  48. const canvas = document.querySelector("canvas"); const ctx = canvas.getContext("2d"); Module.NES =

    { ctx, canvas, image: ctx.createImageData(256, 240) }; mergeInto(LibraryManager.library, { canvas_render: function (ptr, len) { Module.NES.buf = new Uint8Array(Module.HEAPU8.buffer, ptr, len); Module.NES.image.data.set(Module.NES.buf); Module.NES.ctx.putImageData(Module.NES.image, 0, 0); }, }) +BWB4DSJQUଆͷॲཧ FNTDSJQUFOΛ࢖༻͍ͯ͠Δ৔߹ NFSHF*OUPΛ࢖༻ͯ͠+4ؔ਺Λ 3VTUଆʹެ։Ͱ͖Δ
  49. const canvas = document.querySelector("canvas"); const ctx = canvas.getContext("2d"); Module.NES =

    { ctx, canvas, image: ctx.createImageData(256, 240) }; mergeInto(LibraryManager.library, { canvas_render: function (ptr, len) { Module.NES.buf = new Uint8Array(Module.HEAPU8.buffer, ptr, len); Module.NES.image.data.set(Module.NES.buf); Module.NES.ctx.putImageData(Module.NES.image, 0, 0); }, }) +BWB4DSJQUଆͷॲཧ 3VTUଆ͔ΒϐΫηϧσʔλͷ ϙΠϯλͱ௕͞Λ΋Βͬͯ *NBHF%BUBʹηοτ
  50. extern "C" { fn canvas_render(ptr: *const u8, len: usize); }

    impl Renderer { pub fn render( &mut self, background: &BackgroundField, sprites: &SpritesWithCtx) { // …লུ… unsafe { canvas_render(self.buf.as_ptr(), self.buf.len()); } } 3VTU XBTN ଆͷॲཧ ͜Μͳ;͏ʹݺ΂Δ όοϑΝͷϙΠϯλͱ௕͞Λ ڭ͑ͯ͋͛Δ
  51. pub fn run(&mut self, cycle: usize, nmi: &mut bool, mmc:

    &Mmc) -> bool { let cycle = self.cycle + cycle; if cycle < 341 { self.cycle = cycle; return false; } if self.line == 0 { self.background.clear(); } self.cycle = cycle - 341; self.line = self.line + 1; if self.line <= 240 && self.line % 8 == 0 { self.background.build_line( &self.ctx.vram, &self.ctx.cram, &self.ctx.palette, (tile_x, tile_y), ); } // …লུ… } 116ͷ࣮૷ 116αΠΫϧ਺ΛՃࢉ͍͖ͯ͠ MJOF׬͔ྃͨ͠Λ൑ఆ
  52. pub fn run(&mut self, cycle: usize, nmi: &mut bool, mmc:

    &Mmc) -> bool { let cycle = self.cycle + cycle; if cycle < 341 { self.cycle = cycle; return false; } if self.line == 0 { self.background.clear(); } self.cycle = cycle - 341; self.line = self.line + 1; if self.line <= 240 && self.line % 8 == 0 { self.background.build_line( &self.ctx.vram, &self.ctx.cram, &self.ctx.palette, (tile_x, tile_y), ); } // …লུ… } 116ͷ࣮૷ ඳը۠ؒͰ͋Ε͹ ϥΠϯຖʹഎܠΛ ૊Έཱ͍ͯͯ͘ MJOFd
  53. pub fn run(&mut self, cycle: usize, nmi: &mut bool, mmc:

    &Mmc) -> bool { //…লུ… if self.line == 241 { self.registers.set_vblank(); if self.registers.is_irq_enable() { *nmi = true; } } if self.line >= 262 { self.registers.clear_vblank(); self.line = 0; self.sprites = build_sprites( &self.ctx.cram, &self.ctx.sprite_ram, &self.ctx.palette, ); return true; } false } 116ͷ࣮૷ ׂΓࠐΈ༗ޮͰ͋Ε͹WCMBOL ࣌ʹ/.*ΛΞΫςΟϒʹઃఆ 7#MBOLϑϥάΛηοτ MJOF
  54. pub fn run(&mut self, cycle: usize, nmi: &mut bool, mmc:

    &Mmc) -> bool { //…লུ… if self.line == 241 { self.registers.set_vblank(); if self.registers.is_irq_enable() { *nmi = true; } } if self.line >= 262 { self.registers.clear_vblank(); self.line = 0; self.sprites = build_sprites( &self.ctx.cram, &self.ctx.sprite_ram, &self.ctx.palette, ); return true; } false } 116ͷ࣮૷ ϥΠϯͰҰը໘෼ͷ ඳը͕׬੒ MJOF
  55. ࣌ؒ NT NT ը໘෼ͷσʔλ͕ Ͱ͖Δ·Ͱ $16116Λ࣮ߦ ը໘෼ͷσʔλ͕ Ͱ͖Δ·Ͱ $16116Λ࣮ߦ ը໘෼ͷσʔλ͕

    Ͱ͖Δ·Ͱ $16116Λ࣮ߦ ඳը ඳը ඳը ͜ΜͳΠϝʔδͰ࣮ߦ͍ͨ͠ NT $16ͱ116ͷಉظ
  56. void emscripten_set_main_loop( em_callback_func func, int fps, int simulate_infinite_loop) FNTDSJQUFOΛ࢖༻͍ͯ͠Δ৔߹͍͕ͭ͜࢖͑Δ ୈೋҾ਺ʹΛηοτ͢ΔͱΛ

    ࢖ͬͯ͘ΕΔ SFRVFTU"OJNBUJPO'SBNF FNTDSJQUFO@TFU@NBJO@MPPQ ୈࡾҾ਺Ληοτ͢ΔͱແݶϧʔϓΛ γϛϡϨʔτ
  57. MFUNBJO@MPPQcc\ nes::run(&mut ctx); }; externs::set_main_loop_callback(main_loop); pub fn run(ctx: &mut Context)

    { loop { // …লུ… let cycle: u16 = cpu::run(&mut ctx.cpu_registers, &mut cpu_bus, &mut ctx.nmi) as u16 let is_ready = ctx.ppu.run((cycle * 3) as usize, &mut ctx.nmi, &ctx.mmc); if is_ready { if ctx.ppu.background.0.len() != 0 { ctx.renderer.render(&ctx.ppu.background.0, &ctx.ppu.sprites); } break; } } } ϝΠϯϧʔϓͷ࣮૷ NTपظͰOFTSVOΛ࣮ߦ
  58. MFUNBJO@MPPQcc\ nes::run(&mut ctx); }; externs::set_main_loop_callback(main_loop); pub fn run(ctx: &mut Context)

    { loop { // …লུ… let cycle: u16 = cpu::run(&mut ctx.cpu_registers, &mut cpu_bus, &mut ctx.nmi) as u16 let is_ready = ctx.ppu.run((cycle * 3) as usize, &mut ctx.nmi, &ctx.mmc); if is_ready { if ctx.ppu.background.0.len() != 0 { ctx.renderer.render(&ctx.ppu.background.0, &ctx.ppu.sprites); } break; } } } ϝΠϯϧʔϓͷ࣮૷ ߋ৽ͨ͠ը໘ͷϐΫηϧσʔλͷ४උ͕ Ͱ͖Δ·Ͱ$16ͱ116Λ࣮ߦ͢Δ $16ͷ࣮ߦαΠΫϧͷഒͷαΠΫϧΛ ౉͢ʢ116αΠΫϧ΁ͷม׵ʣOΛ࣮ߦ
  59. MFUNBJO@MPPQcc\ nes::run(&mut ctx); }; externs::set_main_loop_callback(main_loop); pub fn run(ctx: &mut Context)

    { loop { // …লུ… let cycle: u16 = cpu::run(&mut ctx.cpu_registers, &mut cpu_bus, &mut ctx.nmi) as u16 let is_ready = ctx.ppu.run((cycle * 3) as usize, &mut ctx.nmi, &ctx.mmc); if is_ready { if ctx.ppu.background.0.len() != 0 { ctx.renderer.render(&ctx.ppu.background.0, &ctx.ppu.sprites); } break; } } } ϝΠϯϧʔϓͷ࣮૷ Ұը໘෼४උ͕Ͱ͖ͨΒϨϯμϦϯά
  60. )FMMP 8PSME char NesMain() { const char palettes[] = {

    0x0f, 0x00, 0x10, 0x20, 0x0f, 0x06, 0x16, 0x26, 0x0f, 0x08, 0x18, 0x28, 0x0f, 0x0a, 0x1a, 0x2a }; const char string[] = "HELLO, WORLD!"; char i; *(char*)0x2000 = 0x00; *(char*)0x2001 = 0x00; *(char*)0x2006 = 0x3f; *(char*)0x2006 = 0x00; for (i = 0; i < 0x10; i ++) *(char*)0x2007 = palettes[i]; *(char*)0x2006 = 0x21; *(char*)0x2006 = 0xc9; for (i = 0; i < 13; i ++) *(char*)0x2007 = string[i]; *(char*)0x2000 = 0x08; *(char*)0x2001 = 0x1e; while (1); return 0; }
  61. char NesMain() { const char palettes[] = { 0x0f, 0x00,

    0x10, 0x20, 0x0f, 0x06, 0x16, 0x26, 0x0f, 0x08, 0x18, 0x28, 0x0f, 0x0a, 0x1a, 0x2a }; const char string[] = "HELLO, WORLD!"; എܠύϨοτσʔλ ৭෼Λࢦఆ ॻ͖ࠐΉจࣈྻ )FMMP 8PSME
  62. *(char*)0x2000 = 0x00; *(char*)0x2001 = 0x00; *(char*)0x2006 = 0x3f; *(char*)0x2006

    = 0x00; for (i = 0; i < 0x10; i ++) *(char*)0x2007 = palettes[i]; ඳը΍ׂΓࠐΈΛېࢭ Y' YΛYʹॻ͖ࠐΉ ͜ͱͰ116ΞυϨεY'Λࢦఆ ͭ·ΓഎܠύϨοτςʔϒϧྖҬ YʹσʔλΛॻ͖͜ΜͰ͍͘͜ͱͰ ઌʹࢦఆͨ͠Y'͔ΒॱʹQBMFUUTͷσʔλΛసૹ͍ͯ͘͠ 116ΞυϨε͸σʔλ͕ॻ͖ࠐ·ΕΔͨͼʹ116ଆͰࣗಈΠϯΫϦϝϯτ͞ΕΔ ͜ΕͰύϨοτσʔλͷసૹ͸0, )FMMP 8PSME
  63. *(char*)0x2006 = 0x21; *(char*)0x2006 = 0xc9; for (i = 0;

    i < 13; i ++) *(char*)0x2007 = string[i]; *(char*)0x2000 = 0x08; *(char*)0x2001 = 0x1e; while (1); return 0; } Y Y$ΛYʹॻ͖ࠐΉ ͜ͱͰ116ΞυϨεY$Λࢦఆ Yʹॻ͖ࠐΉ͜ͱͰ Y$ʙʹA)&--0 803-%A Λసૹ ඳը։࢝ ͜ͷαϯϓϧ͸Ұ౓ը໘Λඳըͨ͠Β ߋ৽͞Εͳ͍ͷͰແݶϧʔϓ )FMMP 8PSME
  64. 0x2000ʙ0x23BF 0x03C0 ωʔϜςʔϒϧ0 0x23C0ʙ0x23FF 0x0040 ଐੑςʔϒϧ0 0x2400ʙ0x27BF 0x03C0 ωʔϜςʔϒϧ1 0x27C0ʙ0x27FF

    0x0040 ଐੑςʔϒϧ1 0x2800ʙ0x2BBF 0x03C0 ωʔϜςʔϒϧ2 0x2BC0ʙ0x2BFF 0x0040 ଐੑςʔϒϧ2 0x2C00ʙ0x2FBF 0x03C0 ωʔϜςʔϒϧ3 0x2FC0ʙ0x2FFF 0x0040 ଐੑςʔϒϧ3 116ͷϝϞϦϚοϓΛݟ௚͢ͱҎԼͷΑ͏ʹωʔϜςʔϒϧͱ ଐੑςʔϒϧͷηοτΛͭʢ͢ͳΘͪը໘෼ʣ࣋ͯΔΑ͏ʹͳ͍ͬͯΔ 116ͷϝϞϦϚοϓ ࠶
  65. 116$53-Ϩδελ ΞυϨε ֓ཁ Y CJU આ໌ ʜ ʜলུʜ ωʔϜςʔϒϧࢦఆ

    116$53-Ϩδελ Y ͷCJU͸ωʔϜςʔϒϧΛࢦఆ͢Δ͜ͱ͕Ͱ͖Δ ωʔϜ ଐੑςʔϒϧ ωʔϜ ଐੑςʔϒϧ ωʔϜ ଐੑςʔϒϧ ωʔϜ ଐੑςʔϒϧ ྫ͑͹ωʔϜςʔϒϧࢦఆ͕CͰ͋Ε͹͜ͷςʔϒϧ͕ࢦఆ͞Εͨ͜ͱʹͳΓ Yํ޲ʹQY෼εΫϩʔϧ͕Φϑηοτͨ͜͠ͱͱಉ౳ͱͳΔ ઌͷ1164$30--ͱ߹ΘͤΕ͹ QYҎ্ͷεΫϩʔϧ͕Մೳ ׂ Ѫ
  66. 116͸εϓϥΠτ༻ͷྖҬεϓϥΠτ3".Λ͍࣋ͬͯΔ 116ͷϝϞϦϚοϓʹ͸දݱ͞Ε͍ͯͳ͍͓ͦ͠Β͘116಺෦ͷྖҬʁ ΞυϨε ֓ཁ Y εϓϥΠτϝϞϦΞυϨε YΛܦ༝ͯ͠εϓϥΠτϝϞϦ΁ϦʔυϥΠτ͢ΔϏοτΞυϨεΛࢦఆ Y εϓϥΠτϝϞϦσʔλ YʹΑͬͯࢦఆ͞ΕͨεϓϥΠτϝϞϦΞυϨεʹ͍ͨͯ͠ϦʔυϥΠτΛߦ͏

    ΞΫηε͢Δͨͼʹ౓ʹεϓϥΠτϝϞϦΞυϨε͸ΠϯΫϦϝϯτ ͞ΕΔɻ ΞΫηεํ๏͸116"%%3116%"5" YY ͱ΄΅ಉ͡ ҟͳΔ఺͸εϓϥΠτ3".ͷۭؒ͸CJUͰදݱͰ͖ΔͷͰ ΞυϨεΛઃఆ͢ΔͨΊʹ̎ճॻ͖ࠐΉඞཁ͸ͳ͍ #ZUF εϓϥΠτ3".
  67. εϓϥΠτͭʹରͯ͠ҎԼͷ#ZUFͷઃఆΛ͍࣋ͬͯΔ #ZUF ֓ཁ :࠲ඪ εϓϥΠτΠϯσοΫε ΩϟϥΫλʔ30.಺ͷεϓϥΠτͷԿ൪໨ͷεϓϥΠτΛద༻͢Δ͔Λࢦఆ͢Δ

    9࠲ඪ CJU આ໌ ਨ௚ํ޲൓స൓స͠ͳ͍൓స͢Δ ਫฏํ޲൓స൓స͠ͳ͍൓స͢Δ දࣔ༏ઌॱҐεϓϥΠτΛ༏ઌഎܠΛ༏ઌ ༧໿ ύϨοτ൪߸CύϨοτCύϨοτ CύϨοτCύϨοτ εϓϥΠτ3".͸#ZUFͳͷͰηοτͰ͖Δ ࠷େεϓϥΠτ εϓϥΠτ3".
  68. ΞυϨε ֓ཁ Y CJU આ໌ 7CMBOL࣌ʹ εϓϥΠτώοτ࣌ʹ

    εΩϟϯϥΠϯεϓϥΠτ਺ɹݸҎԼݸҎ্ 3FTFSWFE εϓϥΠτ̌ώοτ εϓϥΠτ3".ͷઌ಄ʹઃఆ͞ΕͨεϓϥΠτ͕ ϥΠϯόοϑΝʹల։͞ΕΔͱ116Ϩδελ Y ͷʮεϓϥΠτ̌ώοτʯϑϥάཱ͕ͭVOΛ࣮ߦ
  69. ͨͱ͑͹εϓϥΠτ3".#ZUFΛద౰ͳ஋ͰຒΊΔ ؆୯ͳॲཧΛߟ͑ͯΈΔ ldy $FF # 2cycle copysprites: sty $2004 #

    4cycle dey # 2cycle bne copysprites # 2cycle Y DZDMF dΛεϓϥΠτ3". ʹॻ͖ࠐΉ εϓϥΠτͷసૹύϑΥʔϚϯε
  70. const convertKeyCode = keyCode => { switch (keyCode) { case

    88: return 0x01; // X A case 90: return 0x02; // Z B case 65: return 0x04; // A SELECT case 83: return 0x08; // S START case 38: return 0x10; // ↑ ↑ case 40: return 0x20; // ↓ ↓ case 37: return 0x40; // ← ← case 39: return 0x80; // → → } }; document.addEventListener("keydown", e => { buf[size - 1] |= convertKeyCode(e.keyCode); }); document.addEventListener("keyup", e => { buf[size - 1] &= ~convertKeyCode(e.keyCode); }); +BWB4DSJQUଆͷॲཧ LFZEPXOVQΠϕϯτΛ MJTUFO OFTϑΝΠϧͷத਎Λ౉ͨ͢Ίͷ όοϑΝͷ຤ඌʹΩʔೖྗΛ ௨஌͢ΔͨΊͷྖҬ#ZUF෼֬ อͦ͜͠ʹঢ়ଶΛॻ͖ࠐΉ
  71. let main_loop = || { let key_state = buf[len -

    1]; keypad.update(key_state); // …লུ… }; impl Keypad { pub fn new() -> Self { // …লུ… } pub fn update(&mut self, data: Data) { self.buffer = data; } pub fn write(&mut self, data: Data) { // …লུ… } pub fn read(&mut self) -> u8 { let v = (0x01 << self.addr) as u8; let ret = ((self.register & v) >> self.addr) as u8; self.addr += 1; ret } } 3VTU XBTN ଆͷॲཧ ϝΠϯϧʔϓͰ+4͔Βड͚औͬͨ Ωʔͷঢ়ଶΛ൓ө Y͕Πʔυ͞ΕΔͨͼ " # 4&-&$5ʜͷॱʹ ஋Λฦ͢Α͏ʹ͓ͯ͘͠
  72. ΞυϨε 38 ༻్ Y 8 ۣܗ೾$)੍ޚϨδελ Y 8 ۣܗ೾$)੍ޚϨδελ Y

    8 ۣܗ೾$)प೾਺Ϩδελ Y 8 ۣܗ೾$)प೾਺Ϩδελ Y 8 ۣܗ೾$)੍ޚϨδελ Y 8 ۣܗ೾$)੍ޚϨδελ Y 8 ۣܗ೾$)प೾਺Ϩδελ Y 8 ۣܗ೾$)प೾਺Ϩδελ Y 8 ࡾ֯೾੍ޚϨδελ Y" 8 ࡾ֯೾प೾਺Ϩδελ Y# 8 ࡾ֯೾प೾਺Ϩδελ "16ͷϨδελҰཡ ׂ Ѫ
  73. ΞυϨε 38 ༻్ Y$ 8 ϊΠζ੍ޚϨδελ Y& 8 ϊΠζཚ਺Ϩδελ Y'

    8 ϊΠζ࣌ؒϨδελ Y 8 %1$.੍ޚϨδελ Y 8 %1$.੍ޚϨδελ Y 8 %1$.੍ޚϨδελ Y 8 %1$.੍ޚϨδελ Y 38 Ի੠νϟωϧ੍ޚϨδελ Y 38 ϑϨʔϜΧ΢ϯλ "16ͷϨδελҰཡ ׂ Ѫ
  74. ΞυϨε ֓ཁ Y CJU આ໌ σϡʔςΟൺCC CC ࠶ੜ࣌ؒΧ΢ϯλΠωʔϒϧ

    ΤϯϕϩʔϓσΟηʔϒϧ ϘϦϡʔϜ·ͨ͸ΤϯϕϩʔϓσΟόΠμ σϡʔςΟൺ ݮਰͷ଎౓ͳͲΛ੍ޚ ۣܗ೾Ϩδελ ׂ Ѫ
  75. ΞυϨε ֓ཁ Y CJU આ໌ εΠʔϓΠωʔϒϧ प೾਺มԽ଎౓

    प೾਺ͷมԽํ޲Լ্߱ঢ प೾਺มߋྔͷ৔߹มԽͳ͠ प೾਺ͷ૿ݮΛ੍ޚ ۣܗ೾Ϩδελ ׂ Ѫ
  76. ΞυϨε ֓ཁ Y Y CJU આ໌ ύϧελΠϚʔԼҐCJU CJU આ໌

    ΩʔΦϑΧ΢ϯλ ύϧελΠϚʔ্ҐCJU प೾਺ͱ࠶ੜ࣌ؒΛ੍ޚ ۣܗ೾Ϩδελ ׂ Ѫ
  77. class Oscillator { constructor(type) { const AudioContext = window.AudioContext ||

    window.webkitAudioContext this.context = new AudioContext(); this.oscillator = this.createOscillator({ kind: type }); // …লུ… } createOscillator(options = {}) { const oscillator = this.context.createOscillator(); // …লུ… return oscillator; } setPulseWidth(pulseWidth) { this.oscillator.setPeriodicWave(this.waves[`${pulseWidth}`]); } setFrequency(frequency) { this.oscillator.frequency.value = frequency; } changeFrequency(frequency) { this.oscillator.frequency.setValueAtTime(frequency, this.context.currentTime) } } +BWB4DSJQUଆͷॲཧ TFU1FSJPEJD8BWFͰ ΧελϜ೾ܗΛ࡞੒Ͱ͖Δ ׂ Ѫ
  78. mergeInto(LibraryManager.library, { start_oscillator: function (index) { Module.NES.oscs[index].start(); }, 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_pulse_width: function (index, width) { Module.NES.oscs[index].setPulseWidth(width); }, } 3VTU XBTN ଆ΁ͷϒϦοδ ࠶ͼNFSHF*OUPͰ 3VTUଆ΁ެ։ ׂ Ѫ
  79. pub fn update_counters(&mut self) { // …লུ… self.sweep_unit_counter += 1;

    self.change_frequency(); } pub fn update_envelope(&mut self) { // …লུ… unsafe { set_oscillator_volume(self.index, self.get_volume()); }; } fn change_frequency(&self) { unsafe { change_oscillator_frequency(self.index, self.frequency); } } 3VTU XBTN ଆͷॲཧ ׂ Ѫ
  80. $)3 30. $/ )$ "" "" %% 8& )$ͰσʔλΛϥον͠

    ΞυϨεʹม׵ ΩϟϥΫλʔ30. όΠφϦΧ΢ϯλ .BQQFSͷճ࿏
  81. impl Mmc { pub fn new(mapper: u8, bank: u8) ->

    Self { Mmc { bank, mapper } } pub fn set_bank(&mut self, bank: u8) { self.bank = bank; } pub fn create_chram_addr(&self, addr: u16) -> u16 { addr + (self.bank as u16) * 0x2000 } } .BQQFSͷ࣮૷ όϯΫͷઃఆ͔Β ΩϟϥΫλʔ30.΁ͷΞυϨε Λܭࢉͯ͠ฦ͢
  82. DISPNF 8FC"TTFNCMZWT+BWB4DSJQU DISPNFDBOBSZ pSFGPY CJU pSFGPYEFWFMPQFSFEJUJPO C

    CJU TBGBSJ NT NT NT NT NT NT NT NT NT NT UJNF<NT> TNBMMFSJTCFUUFS 8FC"TTFNCMZ CPLVXFCSVTUZOFT +BWB4DSJQU CPLVXFCqPXOFT 8FC"VEJP$BOWBTΛσΟηʔϒϧʹͨ͠ঢ়ଶͰ ϝΠϯϧʔϓͷॲཧ࣌ؒճͷฏۉ஋Λൺֱ .BD#PPL"JS JODI &BSMZ ()[*OUFM$PSFJ(#.)[%%3 HJLPOFT