gocon2019.pdf

83c268781ec54605af1f7b866d7de3ad?s=47 bokuweb
October 28, 2019

 gocon2019.pdf

83c268781ec54605af1f7b866d7de3ad?s=128

bokuweb

October 28, 2019
Tweet

Transcript

  1. (P$POGFSFODF5PLZP (PͰͭ͘Δ(BNF#PZΤϛϡϨʔλ ·ͨ͸ϒϥ΢βͰͷಈ͔͠ํ !CPLVXFC

  2. !CPLVXFC IUUQCMPHCPLVXFCNF w 8FCϑϩϯτΤϯυΤϯδχΞ w ૊ΈࠐΈϋʔυ΢ΣΞΤϯδχΞ w ࢁʹੜଉ CPLVXFC ࣗݾ঺հ

  3. ΰʔϧ ΤϛϡϨʔλͷ࣮૷ͷجຊ΍ 8FC"TTFNCMZ΁ͷม׵ํ๏ Λ஌Δ͜ͱͰָͦ͠͏ͩͳͱ ࢥͬͯ΋Β͑Δͱخ͍͠Ͱ͢

  4. %&.0 IUUQTCPLVXFCHJUIVCJPHPQIFSCPZ

  5. جຊεϖοΫ ߲໨ ֓ཁ CPU LR35902 4.19MHz 8bit RAM 8kB VRAM

    8kB ROM 256k~32MBit Display 4֊ௐϞϊΫϩɺ160×144υοτ εϓϥΠτ 8×8 ࠷େ40ݸදࣔ / 1ϥΠϯ্ʹ ࠷େ10ݸදࣔ എܠ 256×256υοτ α΢ϯυ ۣܗ೾2ch+೾ܗϝϞϦԻݯ1ch+ϊΠζ1ch ௨৴ϙʔτ γϦΞϧ௨৴ϙʔτ౥ࡌ ׂࠐΈػೳ ύουೖྗׂࠐΈɺγϦΞϧ௨৴ׂࠐΈɺλΠϚʔׂࠐΈ LCDCׂࠐΈɺVblankׂࠐΈ
  6. -$% Y (16 "16 $16 73". L# 83". L# $BSUSJEHF

    CJUόε 30. 3". ϒϩοΫਤ -3 .)[
  7. $16

  8. Ϩδελ CJU CJU ֓ཁ " ' ൚༻ϑϥάϨδελ # $ ൚༻Ϩδελ

    % & ʏ ) - ʏ 41 ελοΫϙΠϯλ 1$ ϓϩάϥϜΧ΢ϯλ
  9. ϑϥάʢ'ʣϨδελ CJU ུশ ໊শ ৄࡉ  ; θϩ ԋࢉ݁Ռ͕̌ͳΒ̍ 

    / αϒτϥΫτ Ճࢉ໋ྩͳΒ̌ݮࢉ໋ྩͳΒ̍  ) ϋʔϑΩϟϦʔ ԼҐ̐ϏοτͰ͔Β܁Γ্͛ ্Ґ̐Ϗοτ͔Β܁ΓԼ͕͛ൃੜͨ͠ Β̍  $ ΩϟϦʔ ԋࢉͰΦʔόʔϑϩʔ͕ൃੜͨ͠Β̍   ະ࢖༻ 
  10. Ϩδελ type Registers struct { A byte B byte C

    byte D byte E byte H byte L byte F byte }
  11. Ϩδελ type CPU struct { PC types.Word SP types.Word Regs

    Registers …omitted… }
  12. ϑΣον σίʔυ ࣮ߦ $16ͷجຊಈ࡞

  13. $16ͷجຊಈ࡞ func (cpu *CPU) Step() Cycle { opcode := cpu.fetch()

    inst := instructions[opcode] operands := cpu.fetchOperands( inst.OperandsSize ) inst.Execute(cpu, operands) return inst.Cycles }
  14. $16ͷجຊಈ࡞ func (cpu *CPU) Step() Cycle { opcode := cpu.fetch()

    inst := instructions[opcode] operands := cpu.fetchOperands( inst.OperandsSize ) inst.Execute(cpu, operands) return inst.Cycles }
  15. ϑΣον func (cpu *CPU) fetch() byte { d := cpu.bus.ReadByte(cpu.PC)

    cpu.PC++ return d }
  16. ϑΣον func (cpu *CPU) fetch() byte { d := cpu.bus.ReadByte(cpu.PC)

    cpu.PC++ return d } 1$ʹ֨ೲ͞ΕͨΞυϨε ͔ΒσʔλΛऔಘ
  17. Y Y Y Y Y'' 1$ ϓϩάϥϜΧ΢ϯλ ʜ Y -%#

    O Y" ʜ ϝϞϦ ϑΣον 1$Yͷ৔߹
  18. ϑΣον func (cpu *CPU) fetch() byte { d := cpu.bus.ReadByte(cpu.PC)

    cpu.PC++ return d }
  19. 1$ ϓϩάϥϜΧ΢ϯλ ϑΣον Y Y Y Y Y'' ʜ Y

    -%# O Y" ʜ ϝϞϦ
  20. σίʔυ func (cpu *CPU) Step() Cycle { opcode := cpu.fetch()

    inst := instructions[opcode] operands := cpu.fetchOperands( inst.OperandsSize ) inst.Execute(cpu, operands) return inst.Cycles }
  21. σίʔυ type inst struct { Opcode byte //ɹ໋ྩίʔυɹ Description string

    // ໋ྩ໊ OperandsSize uint // Ҿ਺ͷ਺ Cycles uint // ࣮ߦʹඞཁͳαΠΫϧ਺ Execute func(cpu *CPU, operands []byte) }
  22. σίʔυ var instructions = []*inst{ &inst{0x0, "NOP", 0, 1, func(cpu

    *CPU, operands []byte) { ɹɹ cpu.nop() } }, &inst{0x1, "LD BC,nn", 2, 3, func(cpu *CPU, operands []byte) { cpu.ldn_nn(…) } }, …omitted… &inst{0x6, "LD B,n", 1, 2, func(cpu *CPU, operands []byte) { cpu.ldnn_n(&cpu.Regs.B, operands) } }, …omitted… }
  23. σίʔυ var instructions = []*inst{ &inst{0x0, "NOP", 0, 1, func(cpu

    *CPU, operands []byte) { ɹɹ cpu.nop() } }, &inst{0x1, "LD BC,nn", 2, 3, func(cpu *CPU, operands []byte) { cpu.ldn_nn(…) } }, …omitted… &inst{0x6, "LD B,n", 1, 2, func(cpu *CPU, operands []byte) { cpu.ldnn_n(&cpu.Regs.B, operands) } }, …omitted… } -%# O Φϖϥϯυ਺ $16αΠΫϧ਺ MEOO@Oؔ਺
  24. σίʔυ func (cpu *CPU) Step() Cycle { opcode := cpu.fetch()

    inst := instructions[opcode] operands := cpu.fetchOperands( inst.OperandsSize ) inst.Execute(cpu, operands) return inst.Cycles }
  25. σίʔυ func (cpu *CPU) fetchOperands(size uint) []byte { operands :=

    []byte{} for i := 0; i < int(size); i++ { operands = append(operands, cpu.fetch()) } return operands }
  26. 1$ ϓϩάϥϜΧ΢ϯλ Y Y Y Y Y'' ʜ Y -%#

    O Y" ʜ ϝϞϦ σίʔυ Φϖϥϯυଈ஋Y" ͕Ϧʔυ͞ΕΔ
  27. ࣮ߦ func (cpu *CPU) Step() Cycle { opcode := cpu.fetch()

    inst := instructions[opcode] operands := cpu.fetchOperands( inst.OperandsSize ) inst.Execute(cpu, operands) return inst.Cycles }
  28. ࣮ߦ // LD nn,n // Description: // Put value nn

    into n. // Use with: // nn = B,C,D,E,H,L,BC,DE,HL,SP func (cpu *CPU) ldnn_n(reg *types.Register, operands []byte) { *reg = operands[0] }
  29. #VT.FNPSZ

  30. ϝϞϦϚοϓ L#30.CBOL L#TXJUDIBCMF30. L#73". L#TXJUDIBCMF3". L#83". 83".NJSSPS 0". 4QSJUF 3".

    QFSJQIFSBM *OUFSOBM3". *32&OBCMF3FHJTUFS Y Y Y Y" Y$ Y& Y'& Y'&" Y'' Y''''
  31. ϝϞϦϚοϓ L#30.CBOL L#TXJUDIBCMF30. L#73". L#TXJUDIBCMF3". L#83". 83".NJSSPS 0". 4QSJUF 3".

    QFSJQIFSBM *OUFSOBM3". *32&OBCMF3FHJTUFS Y Y Y Y" Y$ Y& Y'& Y'&" Y'' Y'''' ΧʔτϦοδ಺෦ ͷϝϞϦ
  32. Ϧʔυ func (b *Bus) ReadByte(addr types.Word) byte { switch {

    case addr >= 0x0000 && addr <= 0x7FFF: if b.bootmode && addr < 0x0100 { return BIOS[addr] } return b.cartridge.ReadByte(addr) // Video RAM case addr >= 0x8000 && addr <= 0x9FFF: return b.vRAM.Read(addr - 0x8000) case addr >= 0xA000 && addr <= 0xBFFF: return b.cartridge.ReadByte(addr) // Working RAM case addr >= 0xC000 && addr <= 0xDFFF: return b.wRAM.Read(addr - 0xC000) case … omitted … default: } return 0 }
  33. (16

  34. MJOFQY ࠨ͔Βӈʹ ඳը͍ͯ͘͠ λΠϛϯά -$%

  35. -$% λΠϛϯά MJOFQY ը໘ӈ୺·Ͱ౸ୡͨ͠Β ࣍ͷϥΠϯͷࠨ୺ʹ໭Δ ʢ)CMBOL  ͜ͷظؒΛؚΊΔͱ MJOFDMPDL

  36. -$% λΠϛϯά MJOFQY MJOF

  37. ࣍ͷඳըͷͨΊʹઌ಄΁໭Δ ͜ͷͨΊͷظؒ7#MBOL MJOF෼ͷ࣌ؒ -$% λΠϛϯά MJOFQY ϑϨʔϜ͸   

    DMPDL DMPDL
  38. (16ͷجຊಈ࡞ func (g *GPU) Step(cycles uint) { …omitted… g.clock +=

    cycles if g.clock >= 456 { if g.ly < 144 { g.buildBGTile() } else if g.ly == 144 { g.buildSprites() } else if g.ly >= 144+10 { g.ly = 0 …omitted… } g.ly++ g.clock -= 456 } }
  39. (16ͷجຊಈ࡞ func (g *GPU) Step(cycles uint) { …omitted… g.clock +=

    cycles if g.clock >= 456 { if g.ly < 144 { g.buildBGTile() } else if g.ly == 144 { g.buildSprites() } else if g.ly >= 144+10 { g.ly = 0 …omitted… } g.ly++ g.clock -= 456 } } (16αΠΫϧ਺
  40. func (g *GPU) Step(cycles uint) { …omitted… g.clock += cycles

    if g.clock >= 456 { if g.ly < 144 { g.buildBGTile() } else if g.ly == 144 { g.buildSprites() } else if g.ly >= 144+10 { g.ly = 0 …omitted… } g.ly++ g.clock -= 456 } } (16ͷجຊಈ࡞ ΫϩοΫΛMJOF෼ Χ΢ϯτΞοϓ ඳըதϥΠϯΛࣔ͢ ϨδελΛΠϯΫϦϝϯτ
  41. (16ͷجຊಈ࡞ func (g *GPU) Step(cycles uint) { …omitted… g.clock +=

    cycles if g.clock >= 456 { if g.ly < 144 { g.buildBGTile() } else if g.ly == 144 { g.buildSprites() } else if g.ly >= 144+10 { g.ly = 0 …omitted… } g.ly++ g.clock -= 456 } } දࣔൣғ಺Ͱ͋Ε͹ എܠMJOFߏங
  42. (16ͷجຊಈ࡞ func (g *GPU) Step(cycles uint) { …omitted… g.clock +=

    cycles if g.clock >= 456 { if g.ly < 144 { g.buildBGTile() } else if g.ly == 144 { g.buildSprites() } else if g.ly >= 144+10 { g.ly = 0 …omitted… } g.ly++ g.clock -= 456 } } ࠷ऴϥΠϯͰ εϓϥΠτΛඳը
  43. (16ͷجຊಈ࡞ func (g *GPU) Step(cycles uint) { …omitted… g.clock +=

    cycles if g.clock >= 456 { if g.ly < 144 { g.buildBGTile() } else if g.ly == 144 { g.buildSprites() } else if g.ly >= 144+10 { g.ly = 0 …omitted… } g.ly++ g.clock -= 456 } } 7CMBOLظؒ׬ྃ
  44. ̔#ZUFº#ZUFͰºαΠζͷը૾Λදݱ "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                                                                                                                                                                                                  എܠͷඳը ̌ʙ̏ͷύϨοτ*% ̎CJUͷ৭৘ใ
  45. λΠϧσʔλ Y Y'' Y'' Y λΠϧ൪߸͕̌ʙ·Ͱ ׂΓ౰ͯΒΕΔ

  46. λΠϧϚοϓ QY UJMF QY UJMF Y Y#'' YdY#''Λ YλΠϧ෼ͷ λΠϧ൪߸ͰຒΊΔ

    QY UJMF QY UJMF දࣔՄೳൣғ දࣔൣғ
  47. λΠϧϚοϓ 4DSPMM: Y'' 4DSPMM9 Y'' QY QY දࣔൣғ

  48. σʔλͷՄࢹԽ IUUQTCPLVXFCHJUIVCJPHPQIFSCPZEFCVHIUNM

  49. എܠͷඳը func (g *GPU) buildBGTile() { var tileID int for

    x := 0; x < constants.ScreenWidth; x++ { tileY := ((g.ly + uint(g.scrollY)) % 0x100) / 8 * 32 tileX := x+int(g.scrollX))/8%32 tileID = g.getTileID( tileY, uint(tileX, g.getBGTilemapAddr() ) paletteID := g.getBGPaletteID( tileID, int(g.scrollX%8)+x, (g.ly+uint(g.scrollY))%8 ) rgba := g.getBGPalette(uint(paletteID)) base := ((g.ly)*constants.ScreenWidth + uint(x)) * 4 g.imageData[base] = rgba.R g.imageData[base+1] = rgba.G g.imageData[base+2] = rgba.B g.imageData[base+3] = rgba.A } }
  50. എܠͷඳը func (g *GPU) buildBGTile() { var tileID int for

    x := 0; x < constants.ScreenWidth; x++ { tileY := ((g.ly + uint(g.scrollY)) % 0x100) / 8 * 32 tileX := x+int(g.scrollX))/8%32 tileID = g.getTileID( tileY, uint(tileX, g.getBGTilemapAddr() ) paletteID := g.getBGPaletteID( tileID, int(g.scrollX%8)+x, (g.ly+uint(g.scrollY))%8 ) rgba := g.getBGPalette(uint(paletteID)) base := ((g.ly)*constants.ScreenWidth + uint(x)) * 4 g.imageData[base] = rgba.R g.imageData[base+1] = rgba.G g.imageData[base+2] = rgba.B g.imageData[base+3] = rgba.A } } ϥΠϯ෼
  51. func (g *GPU) buildBGTile() { var tileID int for x

    := 0; x < constants.ScreenWidth; x++ { tileY := ((g.ly + uint(g.scrollY)) % 0x100) / 8 * 32 tileX := x+int(g.scrollX))/8%32 tileID = g.getTileID( tileY, uint(tileX, g.getBGTilemapAddr() ) paletteID := g.getBGPaletteID( tileID, int(g.scrollX%8)+x, (g.ly+uint(g.scrollY))%8 ) rgba := g.getBGPalette(uint(paletteID)) base := ((g.ly)*constants.ScreenWidth + uint(x)) * 4 g.imageData[base] = rgba.R g.imageData[base+1] = rgba.G g.imageData[base+2] = rgba.B g.imageData[base+3] = rgba.A } } എܠͷϥΠϯඳը UJMFͷ9:࠲ඪ
  52. func (g *GPU) buildBGTile() { var tileID int for x

    := 0; x < constants.ScreenWidth; x++ { tileY := ((g.ly + uint(g.scrollY)) % 0x100) / 8 * 32 tileX := x+int(g.scrollX))/8%32 tileID = g.getTileID( tileY, uint(tileX, g.getBGTilemapAddr() ) paletteID := g.getBGPaletteID( tileID, int(g.scrollX%8)+x, (g.ly+uint(g.scrollY))%8 ) rgba := g.getBGPalette(uint(paletteID)) base := ((g.ly)*constants.ScreenWidth + uint(x)) * 4 g.imageData[base] = rgba.R g.imageData[base+1] = rgba.G g.imageData[base+2] = rgba.B g.imageData[base+3] = rgba.A } } എܠͷϥΠϯඳը λΠϧ൪߸Λ 73".͔Βऔಘ λΠϧ൪߸͔Β ύϨοτ൪߸Λऔಘ
  53. എܠͷϥΠϯඳը func (g *GPU) buildBGTile() { var tileID int for

    x := 0; x < constants.ScreenWidth; x++ { tileY := ((g.ly + uint(g.scrollY)) % 0x100) / 8 * 32 tileX := x+int(g.scrollX))/8%32 tileID = g.getTileID( tileY, uint(tileX, g.getBGTilemapAddr() ) paletteID := g.getBGPaletteID( tileID, int(g.scrollX%8)+x, (g.ly+uint(g.scrollY))%8 ) rgba := g.getBGPalette(uint(paletteID)) base := ((g.ly)*constants.ScreenWidth + uint(x)) * 4 g.imageData[base] = rgba.R g.imageData[base+1] = rgba.G g.imageData[base+2] = rgba.B g.imageData[base+3] = rgba.A } } όοϑΝʹ3(#" Λ֨ೲ
  54. ΤϛϡϨʔλ

  55. func (g *GB) Next() []byte { for { cycles :=

    g.cpu.Step() * 4 g.gpu.Step(cycles) …omitted… g.currentCycle += cycles if g.currentCycle >= 70224 { g.win.PollKey() g.currentCycle -= 70224 return g.gpu.GetImageData() } } } ΤϛϡϨʔλͷجຊಈ࡞
  56. func (g *GB) Next() []byte { for { cycles :=

    g.cpu.Step() * 4 g.gpu.Step(cycles) …omitted… g.currentCycle += cycles if g.currentCycle >= 70224 { g.win.PollKey() g.currentCycle -= 70224 return g.gpu.GetImageData() } } } ΤϛϡϨʔλͷجຊಈ࡞ $16Λ໋ྩ෼࣮ߦ $16ΫϩοΫ͸෼प ͳͷͰY
  57. func (g *GB) Next() []byte { for { cycles :=

    g.cpu.Step() * 4 g.gpu.Step(cycles) …omitted… g.currentCycle += cycles if g.currentCycle >= 70224 { g.win.PollKey() g.currentCycle -= 70224 return g.gpu.GetImageData() } } } ΤϛϡϨʔλͷجຊಈ࡞ (16ΛܦաΫϩοΫ਺෼Քಇ
  58. ΤϛϡϨʔλͷجຊಈ࡞ func (g *GB) Next() []byte { for { cycles

    := g.cpu.Step() * 4 g.gpu.Step(cycles) …omitted… g.currentCycle += cycles if g.currentCycle >= 70224 { g.win.PollKey() g.currentCycle -= 70224 return g.gpu.GetImageData() } } } ੵࢉΫϩοΫ਺͕̍ϑϨʔϜ෼ ௒͑ͨΒը૾σʔλΛฦ͢
  59. func (g *GB) Start() { t := time.NewTicker(16 * time.Millisecond)

    for { select { case <-t.C: buf := g.Next() g.win.Render(buf) } } } ΤϛϡϨʔλͷجຊಈ࡞ NTपظͰը૾Λੜ੒
  60. ΤϛϡϨʔλͷجຊಈ࡞ func (w *Window) Render(buf []byte) { imgData := make([]color.RGBA,

    constants.ScreenWidth*constants.ScreenHeight) i := 0 for i*4 < len(buf) { y := constants.ScreenHeight - (i / constants.ScreenWidth) - 1 d := color.RGBA{buf[i*4], buf[i*4+1], buf[i*4+2], buf[i*4+3] imgData[y*constants.ScreenWidth+i%constants.ScreenWidth] = d i++ } w.image.Pix = imgData bg := color.RGBA{R: 0x0F, G: 0x38, B: 0x0F, A: 0xFF} w.win.Clear(bg) spr := pixel.NewSprite(pixel.Picture(w.image), pixel.R(0, 0, constants.ScreenWidth, constants.ScreenHeight)) spr.Draw(w.win, pixel.IM) w.updateCamera() w.win.Update() }
  61. 5FTUJOH

  62. ΤϛϡϨʔλͷςετ

  63. t.Run(tt.name, func(t *testing.T) { emu := setup(“hello.gb”) buf := skipFrame(emu,

    100) file, err := os.Create(“hello” + ".png") defer file.Close() …omitted… img := image.NewRGBA(image.Rect(0, 0, 160, 144)) set(img, buf) if err := png.Encode(file, img); err != nil { …omitted… } }) ΤϛϡϨʔλͷςετ
  64. t.Run(tt.name, func(t *testing.T) { emu := setup(“hello.gb”) buf := skipFrame(emu,

    100) file, err := os.Create(“hello” + ".png") defer file.Close() …omitted… img := image.NewRGBA(image.Rect(0, 0, 160, 144)) set(img, buf) if err := png.Encode(file, img); err != nil { …omitted… } }) ΤϛϡϨʔλͷςετ ೚ҙͷϑϨʔϜͷը૾Λ औಘͰ͖ΔΑ͏ʹ͓ͯ͘͠
  65. t.Run(tt.name, func(t *testing.T) { emu := setup(“hello.gb”) buf := skipFrame(emu,

    100) file, err := os.Create(“hello” + ".png") defer file.Close() …omitted… img := image.NewRGBA(image.Rect(0, 0, 160, 144)) set(img, buf) if err := png.Encode(file, img); err != nil { …omitted… } }) ΤϛϡϨʔλͷςετ QOHͱͯ͠อଘ
  66. ΤϛϡϨʔλͷςετ reg-cli ./test/actual ./test/expect ./test/diff -R ./report.html SFHWJ[SFHDMJΛ࢖ͬͯ 7JTVBM3FHSFTTJPO5FTU

  67. ΤϛϡϨʔλͷςετ ը૾ʹࠩ෼͕͋ͬͨΒ$*Λམͱ͢Α͏ʹ

  68. 8FC"TTFNCMZ

  69. ɾϒϥ΢βͰಈ͘όΠφϦϑΥʔϚοτ ɾ͍ΖΜͳݴޠ͔ΒXBTNΛు͚Δ ɾαΠζ͕খ͘͞ϩʔσΟϯά࣌ؒ΋୹͍ 8FC"TTFNCMZ

  70. ᶃ8BTNͷॳظԽ ᶄ+4ଆ΁ؔ਺ΛFYQPSU ᶅ+4Ͱ30.σʔλΛऔಘ ᶆ8BTNʹ30.σʔλΛ౉͠ ΤϛϡϨʔλ࡞੒ ᶇSFRVFTU"OJNBUJPO'SBNF ͰඳըσʔλΛऔಘ͠$BOWBTʹ ඳը ϒϥ΢βͰಈ࡞ͤ͞Δઓུ

  71. ϒϥ΢βͰಈ࡞ͤ͞Δઓུ ᶃ8BTNͷॳظԽ ᶄ+4ଆ΁ؔ਺ΛFYQPSU ᶅ+4Ͱ30.σʔλΛऔಘ ᶆ8BTNʹ30.σʔλΛ౉͠ ΤϛϡϨʔλ࡞੒ ᶇSFRVFTU"OJNBUJPO'SBNF ͰඳըσʔλΛऔಘ͠$BOWBTʹ ඳը

  72. 8BTN <script src="wasm_exec.js"></script>

  73. const go = new Go(); const res = await fetch("./main.wasm");

    const bytes = await res.arrayBuffer(); const { instance } = await WebAssembly.instantiate( bytes, go.importObject ); go.run(instance); 8BTN+4
  74. const go = new Go(); const res = await fetch("./main.wasm");

    const bytes = await res.arrayBuffer(); const { instance } = await WebAssembly.instantiate( bytes, go.importObject ); go.run(instance); 8BTN+4 XBTN@FYFDKTͰੜ੒͞ΕΔ JNQPSUΦϒδΣΫτΛ౉͢
  75. ᶃ8BTNͷॳظԽ ᶄ+4ଆ΁ؔ਺ΛFYQPSU ᶅ+4Ͱ30.σʔλΛऔಘ ᶆ8BTNʹ30.σʔλΛ౉͠ ΤϛϡϨʔλ࡞੒ ᶇSFRVFTU"OJNBUJPO'SBNF ͰඳըσʔλΛऔಘ͠$BOWBTʹ ඳը ϒϥ΢βͰಈ࡞ͤ͞Δઓུ

  76. func main() { w := js.Global() w.Set("GB", js.FuncOf(newGB)) select {}

    } 8BTN(PMBOH HMPCBMʹ(#Λੜ΍͢
  77. func newGB(this js.Value, args []js.Value) interface{} { buf := []byte{}

    for i := 0; i < args[0].Get("length").Int(); i++ { buf = append(buf, byte(args[0].Index(i).Int())) } cart, err := cartridge.NewCartridge(buf) …omitted… } 8BTN(PMBOH +4͔ΒΧʔτϦοδͷ σʔλΛड͚औΔ
  78. func newGB(this js.Value, args []js.Value) interface{} { …omitted… this.Set("next", js.FuncOf(

    func(this js.Value, args []js.Value) interface{} { img := emu.Next() return js.TypedArrayOf(img) } )) …omitted… return this } 8BTN(PMBOH
  79. func newGB(this js.Value, args []js.Value) interface{} { …omitted… this.Set("next", js.FuncOf(

    func(this js.Value, args []js.Value) interface{} { img := emu.Next() return js.TypedArrayOf(img) } )) …omitted… return this } 8BTN(PMBOH OFYUͱ͍͏ؔ਺Λ+4ଆʹFYQPSU
  80. func newGB(this js.Value, args []js.Value) interface{} { …omitted… this.Set("next", js.FuncOf(

    func(this js.Value, args []js.Value) interface{} { img := emu.Next() return js.TypedArrayOf(img) } )) …omitted… return this } 8BTN(PMBOH ࣍ͷϑϨʔϜΛ5ZQFE"SSBZͱͯ͠ฦ͢
  81. ᶃ8BTNͷॳظԽ ᶄ+4ଆ΁ؔ਺ΛFYQPSU ᶅ+4Ͱ30.σʔλΛऔಘ ᶆ8BTNʹ30.σʔλΛ౉͠ ΤϛϡϨʔλ࡞੒ ᶇSFRVFTU"OJNBUJPO'SBNF ͰඳըσʔλΛऔಘ͠$BOWBTʹ ඳը ϒϥ΢βͰಈ࡞ͤ͞Δઓུ

  82. const rom = await fetch("./tobu.gb"); const buf = await rom.arrayBuffer();

    8BTN+4
  83. ᶃ8BTNͷॳظԽ ᶄ+4ଆ΁ؔ਺ΛFYQPSU ᶅ+4Ͱ30.σʔλΛऔಘ ᶆ8BTNʹ30.σʔλΛ౉͠ ΤϛϡϨʔλ࡞੒ ᶇSFRVFTU"OJNBUJPO'SBNF ͰඳըσʔλΛऔಘ͠$BOWBTʹ ඳը ϒϥ΢βͰಈ࡞ͤ͞Δઓུ

  84. let gb = new GB(new Uint8Array(buf)); 8BTN+4

  85. ᶃ8BTNͷॳظԽ ᶄ+4ଆ΁ؔ਺ΛFYQPSU ᶅ+4Ͱ30.σʔλΛऔಘ ᶆ8BTNʹ30.σʔλΛ౉͠ ΤϛϡϨʔλ࡞੒ ᶇSFRVFTU"OJNBUJPO'SBNF ͰඳըσʔλΛऔಘ͠$BOWBTʹ ඳը ϒϥ΢βͰಈ࡞ͤ͞Δઓུ

  86. const frame = () => { const img = gb.next();

    image.data.set(img); ctx.putImageData(image, 0, 0); requestAnimationFrame(frame); }; frame(); 8BTN+4
  87. GOOS=js GOARCH=wasm go build -tags=wasm -oɹ“main.wasm” "gopher-boy/wasm_main.go" Ϗϧυ

  88. .# αΠζ wasm ,# wasm_exec.js

  89. ύϑΥʔϚϯε HP ϒϥ΢β ̍'SBNF NT $ISPNF NT 'JSF'PY NT 4BGBSJ

    NT MacBook Air (Retina, 13-inch, 2018) 1.6 GHz Intel Core i5 16 GB 2133 MHz LPDDR3 go1.12.12 TobuTobuGirl
  90. ύϑΥʔϚϯε 19.11msec

  91. func (cpu *CPU) applyZeroBy(computed byte) { if computed == 0

    { cpu.setFlag(Z) } else { cpu.clearFlag(Z) } } BQQMZ;FSP#Z HP
  92. 8BTNXBU wasm2wat main.wasm -o main.wat

  93. BQQMZ;FSP#Z XBU (func $github.com_bokuweb_gopher_boy_pkg_cpu.__CPU_.applyZeroBy (type 0) (result i32) loop ;;

    label = @1 block ;; label = @2 block ;; label = @3 block ;; label = @4 block ;; label = @5 block ;; label = @6 block ;; label = @7 block ;; label = @8 global.get 1 br_table 0 (;@8;) 0 (;@8;) 1 (;@7;) 2 (;@6;) 3 (;@5;) 3 (;@5;) 4 (;@4;) 4 (;@4;) 5 (;@3;) 6 (;@2;) end global.get 2 global.get 4 i32.wrap_i64 i32.load offset=16 i32.le_u if ;; label = @8 global.get 2 i32.const 8 i32.sub global.set 2 global.get 2 i64.const 390397952 i64.store i32.const 0 global.set 1 call $runtime.morestack_noctxt if ;; label = @9 i32.const 1 return end end global.get 2 i32.const 16 i32.sub global.set 2 global.get 2 i64.load8_u offset=32 i64.const 56 i64.shl i64.const 56 i64.shr_u i64.eqz i64.extend_i32_u i32.wrap_i64 i32.eqz if ;; label = @8 i32.const 6 global.set 1 br 7 (;@1;) end end global.get 2 global.get 2 i64.load offset=24 i64.store global.get 2 i64.const 4 i32.const 8 i32.sub global.set 2 global.get 2 i64.const 390397955 i64.store i32.const 0 global.set 1 call $github.com_bokuweb_gopher_boy_pkg_cpu.__CPU_.setFlag if ;; label = @7 i32.const 1 return end end end global.get 2 i32.const 16 i32.add global.set 2 global.get 2 i32.load16_u offset=2 global.set 0 global.get 2 i32.load16_u global.set 1 global.get 2 i32.const 8 i32.add global.set 2 i32.const 0 return end global.get 2 global.get 2 i64.load offset=24 i64.store global.get 2 i64.const 4 i64.store offset=8 global.get 2 i32.const 8 i32.sub global.set 2 global.get 2 i64.const 390397960 i64.store i32.const 0 global.set 1 call $github.com_bokuweb_gopher_boy_pkg_cpu.__CPU_.clearFlag if ;; label = @4 i32.const 1 return end end i32.const 4 global.set 1 br 1 (;@1;) end end unreachable)
  94. ͍͞͝ʹ ɾήʔϜϘʔΠ͕݁ߏγϯϓϧ ɹͳͷͰ͓͢͢Ί ɾ8BTN͸ݱ࣌఺ͰαΠζ΍ ɹύϑΥʔϚϯεʹߟྀ͕ඞཁ ɹ UJOZHPͳͲʹظ଴  ɾࣗ෼΋΍ͬͯΈΑ͏͔ͱ ɹࢥ͍͚ͬͯͨͩͨΒ޾͍Ͱ͢

  95. ͋Γ͕ͱ͏͍͟͝·ͨ͠✨