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

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

Sponsored · Your Podcast. Everywhere. Effortlessly. Share. Educate. Inspire. Entertain. You do you. We'll handle the rest.
Avatar for bokuweb bokuweb
September 07, 2018
45k

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

Avatar for bokuweb

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&Ͱ͋Ε͹ @@@CͳͷͰҎԼͷΑ͏ʹϒϩοΫʹద༻͞ΕΔύϨοτ͕ܾఆ͢Δ ԾʹҎԼͷύϨοτ͕ઃఆ͞Ε͍ͯͨͱ͢Δͱ λΠϧ ϒϩοΫ λΠϧ ϒϩοΫ   

     ͜ͷϒϩοΫ Y ͸ ͳͷͰ͕ ࢖༻Ͱ͖Δ ύϨοτ  ͜ͷϒϩοΫ͸ ͜ͷϒϩοΫ͸ ͜ͷϒϩοΫ͸ ଐੑςʔϒϧͱ͸
  40. ͨͱ͑͹εύʔϚϦΦϒϥβʔεͷΦʔϓχϯάը໘͸ҎԼͷΑ͏ͳ ύϨοτ͕ઃఆ͞Ε͍ͯΔ ύϨοτY' ύϨοτY' ύϨοτY' ύϨοτY'$ ύϨοτY' ύϨοτY' ύϨοτY' ύϨοτY'$

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

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

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

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

                                                                Y ֘౰ͷ࠲ඪ͸ଐੑςʔϒϧͷΞυϨεY%"ʹ֘౰͢ΔͨΊ 73".ͷ͔ΒΞτϦϏϡʔτ͕Ϧʔυ͞ΕΔ Y%"൪஍ Y 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ඳըखॱΠϝʔδ ࠲ඪͱଐੑςʔϒϧ͔Β ΞτϦϏϡʔτΛࢉग़ ΞτϦϏϡʔτͱϒϩοΫ൪߸͔Β ύϨοτ൪߸Λࢉग़
  47. 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ͷϐΫηϧσʔλΛ ࡞੒
  48. 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(#"ͷϐΫηϧσʔλ ʹม׵
  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ଆͷॲཧ FNTDSJQUFOΛ࢖༻͍ͯ͠Δ৔߹ NFSHF*OUPΛ࢖༻ͯ͠+4ؔ਺Λ 3VTUଆʹެ։Ͱ͖Δ
  50. 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ʹηοτ
  51. 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 ଆͷॲཧ ͜Μͳ;͏ʹݺ΂Δ όοϑΝͷϙΠϯλͱ௕͞Λ ڭ͑ͯ͋͛Δ
  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ͷ࣮૷ 116αΠΫϧ਺ΛՃࢉ͍͖ͯ͠ MJOF׬͔ྃͨ͠Λ൑ఆ
  53. 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
  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ͷ࣮૷ ׂΓࠐΈ༗ޮͰ͋Ε͹WCMBOL ࣌ʹ/.*ΛΞΫςΟϒʹઃఆ 7#MBOLϑϥάΛηοτ MJOF
  55. 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
  56. ࣌ؒ NT NT ը໘෼ͷσʔλ͕ Ͱ͖Δ·Ͱ $16116Λ࣮ߦ ը໘෼ͷσʔλ͕ Ͱ͖Δ·Ͱ $16116Λ࣮ߦ ը໘෼ͷσʔλ͕

    Ͱ͖Δ·Ͱ $16116Λ࣮ߦ ඳը ඳը ඳը ͜ΜͳΠϝʔδͰ࣮ߦ͍ͨ͠ NT $16ͱ116ͷಉظ
  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