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

gocon2019.pdf

Sponsored · SiteGround - Reliable hosting with speed, security, and support you can count on.
Avatar for bokuweb bokuweb
October 28, 2019

 gocon2019.pdf

Avatar for bokuweb

bokuweb

October 28, 2019
Tweet

More Decks by bokuweb

Other Decks in Programming

Transcript

  1. جຊεϖοΫ ߲໨ ֓ཁ 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ׂࠐΈ
  2. -$% Y (16 "16 $16 73". L# 83". L# $BSUSJEHF

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

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

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

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

    byte D byte E byte H byte L byte F byte }
  7. $16ͷجຊಈ࡞ func (cpu *CPU) Step() Cycle { opcode := cpu.fetch()

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

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

    cpu.PC++ return d } 1$ʹ֨ೲ͞ΕͨΞυϨε ͔ΒσʔλΛऔಘ
  10. σίʔυ func (cpu *CPU) Step() Cycle { opcode := cpu.fetch()

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

    // ໋ྩ໊ OperandsSize uint // Ҿ਺ͷ਺ Cycles uint // ࣮ߦʹඞཁͳαΠΫϧ਺ Execute func(cpu *CPU, operands []byte) }
  12. σίʔυ 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… }
  13. σίʔυ 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ؔ਺
  14. σίʔυ 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) fetchOperands(size uint) []byte { operands :=

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

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

    inst := instructions[opcode] operands := cpu.fetchOperands( inst.OperandsSize ) inst.Execute(cpu, operands) return inst.Cycles }
  18. ࣮ߦ // 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] }
  19. ϝϞϦϚοϓ 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''''
  20. ϝϞϦϚοϓ 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'''' ΧʔτϦοδ಺෦ ͷϝϞϦ
  21. Ϧʔυ 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 }
  22. (16

  23. (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 } }
  24. (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αΠΫϧ਺
  25. 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෼ Χ΢ϯτΞοϓ ඳըதϥΠϯΛࣔ͢ ϨδελΛΠϯΫϦϝϯτ
  26. (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ߏங
  27. (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 } } ࠷ऴϥΠϯͰ εϓϥΠτΛඳը
  28. (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ظؒ׬ྃ
  29. ̔#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ͷ৭৘ใ
  30. എܠͷඳը 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 } }
  31. എܠͷඳը 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 } } ϥΠϯ෼
  32. 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:࠲ඪ
  33. 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".͔Βऔಘ λΠϧ൪߸͔Β ύϨοτ൪߸Λऔಘ
  34. എܠͷϥΠϯඳը 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(#" Λ֨ೲ
  35. 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() } } } ΤϛϡϨʔλͷجຊಈ࡞
  36. 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
  37. 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ΛܦաΫϩοΫ਺෼Քಇ
  38. ΤϛϡϨʔλͷجຊಈ࡞ 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() } } } ੵࢉΫϩοΫ਺͕̍ϑϨʔϜ෼ ௒͑ͨΒը૾σʔλΛฦ͢
  39. func (g *GB) Start() { t := time.NewTicker(16 * time.Millisecond)

    for { select { case <-t.C: buf := g.Next() g.win.Render(buf) } } } ΤϛϡϨʔλͷجຊಈ࡞ NTपظͰը૾Λੜ੒
  40. ΤϛϡϨʔλͷجຊಈ࡞ 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() }
  41. 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… } }) ΤϛϡϨʔλͷςετ
  42. 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… } }) ΤϛϡϨʔλͷςετ ೚ҙͷϑϨʔϜͷը૾Λ औಘͰ͖ΔΑ͏ʹ͓ͯ͘͠
  43. 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ͱͯ͠อଘ
  44. 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
  45. 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ΦϒδΣΫτΛ౉͢
  46. 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͔ΒΧʔτϦοδͷ σʔλΛड͚औΔ
  47. 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
  48. 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
  49. 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ͱͯ͠ฦ͢
  50. const frame = () => { const img = gb.next();

    image.data.set(img); ctx.putImageData(image, 0, 0); requestAnimationFrame(frame); }; frame(); 8BTN+4
  51. ύϑΥʔϚϯε 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
  52. func (cpu *CPU) applyZeroBy(computed byte) { if computed == 0

    { cpu.setFlag(Z) } else { cpu.clearFlag(Z) } } BQQMZ;FSP#Z HP
  53. 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)