Slide 1

Slide 1 text

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

Slide 2

Slide 2 text

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

Slide 3

Slide 3 text

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

Slide 4

Slide 4 text

%&.0 IUUQTCPLVXFCHJUIVCJPHPQIFSCPZ

Slide 5

Slide 5 text

جຊεϖοΫ ߲໨ ֓ཁ 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ׂࠐΈ

Slide 6

Slide 6 text

-$% Y (16 "16 $16 73". L# 83". L# $BSUSJEHF CJUόε 30. 3". ϒϩοΫਤ -3 .)[

Slide 7

Slide 7 text

$16

Slide 8

Slide 8 text

Ϩδελ CJU CJU ֓ཁ " ' ൚༻ϑϥάϨδελ # $ ൚༻Ϩδελ % & ʏ ) - ʏ 41 ελοΫϙΠϯλ 1$ ϓϩάϥϜΧ΢ϯλ

Slide 9

Slide 9 text

ϑϥάʢ'ʣϨδελ CJU ུশ ໊শ ৄࡉ ; θϩ ԋࢉ݁Ռ͕̌ͳΒ̍ / αϒτϥΫτ Ճࢉ໋ྩͳΒ̌ݮࢉ໋ྩͳΒ̍ ) ϋʔϑΩϟϦʔ ԼҐ̐ϏοτͰ͔Β܁Γ্͛ ্Ґ̐Ϗοτ͔Β܁ΓԼ͕͛ൃੜͨ͠ Β̍ $ ΩϟϦʔ ԋࢉͰΦʔόʔϑϩʔ͕ൃੜͨ͠Β̍ ະ࢖༻

Slide 10

Slide 10 text

Ϩδελ type Registers struct { A byte B byte C byte D byte E byte H byte L byte F byte }

Slide 11

Slide 11 text

Ϩδελ type CPU struct { PC types.Word SP types.Word Regs Registers …omitted… }

Slide 12

Slide 12 text

ϑΣον σίʔυ ࣮ߦ $16ͷجຊಈ࡞

Slide 13

Slide 13 text

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

Slide 14

Slide 14 text

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

Slide 15

Slide 15 text

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

Slide 16

Slide 16 text

ϑΣον func (cpu *CPU) fetch() byte { d := cpu.bus.ReadByte(cpu.PC) cpu.PC++ return d } 1$ʹ֨ೲ͞ΕͨΞυϨε ͔ΒσʔλΛऔಘ

Slide 17

Slide 17 text

Y Y Y Y Y'' 1$ ϓϩάϥϜΧ΢ϯλ ʜ Y -%# O Y" ʜ ϝϞϦ ϑΣον 1$Yͷ৔߹

Slide 18

Slide 18 text

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

Slide 19

Slide 19 text

1$ ϓϩάϥϜΧ΢ϯλ ϑΣον Y Y Y Y Y'' ʜ Y -%# O Y" ʜ ϝϞϦ

Slide 20

Slide 20 text

σίʔυ func (cpu *CPU) Step() Cycle { opcode := cpu.fetch() inst := instructions[opcode] operands := cpu.fetchOperands( inst.OperandsSize ) inst.Execute(cpu, operands) return inst.Cycles }

Slide 21

Slide 21 text

σίʔυ type inst struct { Opcode byte //ɹ໋ྩίʔυɹ Description string // ໋ྩ໊ OperandsSize uint // Ҿ਺ͷ਺ Cycles uint // ࣮ߦʹඞཁͳαΠΫϧ਺ Execute func(cpu *CPU, operands []byte) }

Slide 22

Slide 22 text

σίʔυ 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… }

Slide 23

Slide 23 text

σίʔυ 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ؔ਺

Slide 24

Slide 24 text

σίʔυ func (cpu *CPU) Step() Cycle { opcode := cpu.fetch() inst := instructions[opcode] operands := cpu.fetchOperands( inst.OperandsSize ) inst.Execute(cpu, operands) return inst.Cycles }

Slide 25

Slide 25 text

σίʔυ func (cpu *CPU) fetchOperands(size uint) []byte { operands := []byte{} for i := 0; i < int(size); i++ { operands = append(operands, cpu.fetch()) } return operands }

Slide 26

Slide 26 text

1$ ϓϩάϥϜΧ΢ϯλ Y Y Y Y Y'' ʜ Y -%# O Y" ʜ ϝϞϦ σίʔυ Φϖϥϯυଈ஋Y" ͕Ϧʔυ͞ΕΔ

Slide 27

Slide 27 text

࣮ߦ func (cpu *CPU) Step() Cycle { opcode := cpu.fetch() inst := instructions[opcode] operands := cpu.fetchOperands( inst.OperandsSize ) inst.Execute(cpu, operands) return inst.Cycles }

Slide 28

Slide 28 text

࣮ߦ // 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] }

Slide 29

Slide 29 text

#VT.FNPSZ

Slide 30

Slide 30 text

ϝϞϦϚοϓ 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''''

Slide 31

Slide 31 text

ϝϞϦϚοϓ 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'''' ΧʔτϦοδ಺෦ ͷϝϞϦ

Slide 32

Slide 32 text

Ϧʔυ 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 }

Slide 33

Slide 33 text

(16

Slide 34

Slide 34 text

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

Slide 35

Slide 35 text

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

Slide 36

Slide 36 text

-$% λΠϛϯά MJOFQY MJOF

Slide 37

Slide 37 text

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

Slide 38

Slide 38 text

(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 } }

Slide 39

Slide 39 text

(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αΠΫϧ਺

Slide 40

Slide 40 text

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෼ Χ΢ϯτΞοϓ ඳըதϥΠϯΛࣔ͢ ϨδελΛΠϯΫϦϝϯτ

Slide 41

Slide 41 text

(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ߏங

Slide 42

Slide 42 text

(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 } } ࠷ऴϥΠϯͰ εϓϥΠτΛඳը

Slide 43

Slide 43 text

(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ظؒ׬ྃ

Slide 44

Slide 44 text

̔#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ͷ৭৘ใ

Slide 45

Slide 45 text

λΠϧσʔλ Y Y'' Y'' Y λΠϧ൪߸͕̌ʙ·Ͱ ׂΓ౰ͯΒΕΔ

Slide 46

Slide 46 text

λΠϧϚοϓ QY UJMF QY UJMF Y Y#'' YdY#''Λ YλΠϧ෼ͷ λΠϧ൪߸ͰຒΊΔ QY UJMF QY UJMF දࣔՄೳൣғ දࣔൣғ

Slide 47

Slide 47 text

λΠϧϚοϓ 4DSPMM: Y'' 4DSPMM9 Y'' QY QY දࣔൣғ

Slide 48

Slide 48 text

σʔλͷՄࢹԽ IUUQTCPLVXFCHJUIVCJPHPQIFSCPZEFCVHIUNM

Slide 49

Slide 49 text

എܠͷඳը 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 } }

Slide 50

Slide 50 text

എܠͷඳը 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 } } ϥΠϯ෼

Slide 51

Slide 51 text

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:࠲ඪ

Slide 52

Slide 52 text

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".͔Βऔಘ λΠϧ൪߸͔Β ύϨοτ൪߸Λऔಘ

Slide 53

Slide 53 text

എܠͷϥΠϯඳը 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(#" Λ֨ೲ

Slide 54

Slide 54 text

ΤϛϡϨʔλ

Slide 55

Slide 55 text

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() } } } ΤϛϡϨʔλͷجຊಈ࡞

Slide 56

Slide 56 text

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

Slide 57

Slide 57 text

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ΛܦաΫϩοΫ਺෼Քಇ

Slide 58

Slide 58 text

ΤϛϡϨʔλͷجຊಈ࡞ 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() } } } ੵࢉΫϩοΫ਺͕̍ϑϨʔϜ෼ ௒͑ͨΒը૾σʔλΛฦ͢

Slide 59

Slide 59 text

func (g *GB) Start() { t := time.NewTicker(16 * time.Millisecond) for { select { case <-t.C: buf := g.Next() g.win.Render(buf) } } } ΤϛϡϨʔλͷجຊಈ࡞ NTपظͰը૾Λੜ੒

Slide 60

Slide 60 text

ΤϛϡϨʔλͷجຊಈ࡞ 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() }

Slide 61

Slide 61 text

5FTUJOH

Slide 62

Slide 62 text

ΤϛϡϨʔλͷςετ

Slide 63

Slide 63 text

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… } }) ΤϛϡϨʔλͷςετ

Slide 64

Slide 64 text

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… } }) ΤϛϡϨʔλͷςετ ೚ҙͷϑϨʔϜͷը૾Λ औಘͰ͖ΔΑ͏ʹ͓ͯ͘͠

Slide 65

Slide 65 text

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ͱͯ͠อଘ

Slide 66

Slide 66 text

ΤϛϡϨʔλͷςετ reg-cli ./test/actual ./test/expect ./test/diff -R ./report.html SFHWJ[SFHDMJΛ࢖ͬͯ 7JTVBM3FHSFTTJPO5FTU

Slide 67

Slide 67 text

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

Slide 68

Slide 68 text

8FC"TTFNCMZ

Slide 69

Slide 69 text

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

Slide 70

Slide 70 text

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

Slide 71

Slide 71 text

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

Slide 72

Slide 72 text

8BTN

Slide 73

Slide 73 text

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

Slide 74

Slide 74 text

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ΦϒδΣΫτΛ౉͢

Slide 75

Slide 75 text

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

Slide 76

Slide 76 text

func main() { w := js.Global() w.Set("GB", js.FuncOf(newGB)) select {} } 8BTN(PMBOH HMPCBMʹ(#Λੜ΍͢

Slide 77

Slide 77 text

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͔ΒΧʔτϦοδͷ σʔλΛड͚औΔ

Slide 78

Slide 78 text

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

Slide 79

Slide 79 text

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

Slide 80

Slide 80 text

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ͱͯ͠ฦ͢

Slide 81

Slide 81 text

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

Slide 82

Slide 82 text

const rom = await fetch("./tobu.gb"); const buf = await rom.arrayBuffer(); 8BTN+4

Slide 83

Slide 83 text

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

Slide 84

Slide 84 text

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

Slide 85

Slide 85 text

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

Slide 86

Slide 86 text

const frame = () => { const img = gb.next(); image.data.set(img); ctx.putImageData(image, 0, 0); requestAnimationFrame(frame); }; frame(); 8BTN+4

Slide 87

Slide 87 text

GOOS=js GOARCH=wasm go build -tags=wasm -oɹ“main.wasm” "gopher-boy/wasm_main.go" Ϗϧυ

Slide 88

Slide 88 text

.# αΠζ wasm ,# wasm_exec.js

Slide 89

Slide 89 text

ύϑΥʔϚϯε 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

Slide 90

Slide 90 text

ύϑΥʔϚϯε 19.11msec

Slide 91

Slide 91 text

func (cpu *CPU) applyZeroBy(computed byte) { if computed == 0 { cpu.setFlag(Z) } else { cpu.clearFlag(Z) } } BQQMZ;FSP#Z HP

Slide 92

Slide 92 text

8BTNXBU wasm2wat main.wasm -o main.wat

Slide 93

Slide 93 text

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)

Slide 94

Slide 94 text

͍͞͝ʹ ɾήʔϜϘʔΠ͕݁ߏγϯϓϧ ɹͳͷͰ͓͢͢Ί ɾ8BTN͸ݱ࣌఺ͰαΠζ΍ ɹύϑΥʔϚϯεʹߟྀ͕ඞཁ ɹ UJOZHPͳͲʹظ଴ ɾࣗ෼΋΍ͬͯΈΑ͏͔ͱ ɹࢥ͍͚ͬͯͨͩͨΒ޾͍Ͱ͢

Slide 95

Slide 95 text

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