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

gocon2019.pdf

bokuweb
October 28, 2019

 gocon2019.pdf

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)