Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Speaker Deck
PRO
Sign in
Sign up
for free
gocon2019.pdf
bokuweb
October 28, 2019
Programming
4
6.9k
gocon2019.pdf
bokuweb
October 28, 2019
Tweet
Share
More Decks by bokuweb
See All by bokuweb
bokuweb
1
770
bokuweb
58
37k
bokuweb
7
3.9k
Other Decks in Programming
See All in Programming
hr01
1
1.2k
kyoheig3
0
420
hanhan1978
0
290
adoranwodo
0
220
akatsukinewgrad
0
170
kubode
0
190
kyonmm
2
2.1k
daiki1020
0
1.1k
nbkouhou
1
1.1k
akatsukinewgrad
0
160
hr01
0
1.6k
momofff
0
160
Featured
See All Featured
jnunemaker
PRO
40
4.5k
myddelton
109
11k
jcasabona
7
520
brad_frost
156
6.4k
addyosmani
1348
190k
ddemaree
274
31k
lemiorhan
626
42k
philhawksworth
192
8.8k
gr2m
83
11k
jrom
114
7.1k
keavy
106
14k
malarkey
192
8.5k
Transcript
(P$POGFSFODF5PLZP (PͰͭ͘Δ(BNF#PZΤϛϡϨʔλ ·ͨϒϥβͰͷಈ͔͠ํ !CPLVXFC
!CPLVXFC IUUQCMPHCPLVXFCNF w 8FCϑϩϯτΤϯυΤϯδχΞ w ΈࠐΈϋʔυΣΞΤϯδχΞ w ࢁʹੜଉ CPLVXFC ࣗݾհ
ΰʔϧ ΤϛϡϨʔλͷ࣮ͷجຊ 8FC"TTFNCMZͷมํ๏ ΛΔ͜ͱͰָͦ͠͏ͩͳͱ ࢥͬͯΒ͑Δͱخ͍͠Ͱ͢
%&.0 IUUQTCPLVXFCHJUIVCJPHPQIFSCPZ
جຊεϖοΫ ߲ ֓ཁ 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ׂࠐΈ
-$% Y (16 "16 $16 73". L# 83". L# $BSUSJEHF
CJUόε 30. 3". ϒϩοΫਤ -3 .)[
$16
Ϩδελ CJU CJU ֓ཁ " ' ൚༻ϑϥάϨδελ # $ ൚༻Ϩδελ
% & ʏ ) - ʏ 41 ελοΫϙΠϯλ 1$ ϓϩάϥϜΧϯλ
ϑϥάʢ'ʣϨδελ CJU ུশ ໊শ ৄࡉ ; θϩ ԋࢉ݁Ռ͕̌ͳΒ̍
/ αϒτϥΫτ Ճࢉ໋ྩͳΒ̌ݮࢉ໋ྩͳΒ̍ ) ϋʔϑΩϟϦʔ ԼҐ̐ϏοτͰ͔Β܁Γ্͛ ্Ґ̐Ϗοτ͔Β܁ΓԼ͕͛ൃੜͨ͠ Β̍ $ ΩϟϦʔ ԋࢉͰΦʔόʔϑϩʔ͕ൃੜͨ͠Β̍ ະ༻
Ϩδελ type Registers struct { A byte B byte C
byte D byte E byte H byte L byte F byte }
Ϩδελ type CPU struct { PC types.Word SP types.Word Regs
Registers …omitted… }
ϑΣον σίʔυ ࣮ߦ $16ͷجຊಈ࡞
$16ͷجຊಈ࡞ func (cpu *CPU) Step() Cycle { opcode := cpu.fetch()
inst := instructions[opcode] operands := cpu.fetchOperands( inst.OperandsSize ) inst.Execute(cpu, operands) return inst.Cycles }
$16ͷجຊಈ࡞ func (cpu *CPU) Step() Cycle { opcode := cpu.fetch()
inst := instructions[opcode] operands := cpu.fetchOperands( inst.OperandsSize ) inst.Execute(cpu, operands) return inst.Cycles }
ϑΣον func (cpu *CPU) fetch() byte { d := cpu.bus.ReadByte(cpu.PC)
cpu.PC++ return d }
ϑΣον func (cpu *CPU) fetch() byte { d := cpu.bus.ReadByte(cpu.PC)
cpu.PC++ return d } 1$ʹ֨ೲ͞ΕͨΞυϨε ͔ΒσʔλΛऔಘ
Y Y Y Y Y'' 1$ ϓϩάϥϜΧϯλ ʜ Y -%#
O Y" ʜ ϝϞϦ ϑΣον 1$Yͷ߹
ϑΣον func (cpu *CPU) fetch() byte { d := cpu.bus.ReadByte(cpu.PC)
cpu.PC++ return d }
1$ ϓϩάϥϜΧϯλ ϑΣον Y Y Y Y Y'' ʜ Y
-%# O Y" ʜ ϝϞϦ
σίʔυ func (cpu *CPU) Step() Cycle { opcode := cpu.fetch()
inst := instructions[opcode] operands := cpu.fetchOperands( inst.OperandsSize ) inst.Execute(cpu, operands) return inst.Cycles }
σίʔυ type inst struct { Opcode byte //ɹ໋ྩίʔυɹ Description string
// ໋ྩ໊ OperandsSize uint // Ҿͷ Cycles uint // ࣮ߦʹඞཁͳαΠΫϧ Execute func(cpu *CPU, operands []byte) }
σίʔυ 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… }
σίʔυ 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ؔ
σίʔυ func (cpu *CPU) Step() Cycle { opcode := cpu.fetch()
inst := instructions[opcode] operands := cpu.fetchOperands( inst.OperandsSize ) inst.Execute(cpu, operands) return inst.Cycles }
σίʔυ func (cpu *CPU) fetchOperands(size uint) []byte { operands :=
[]byte{} for i := 0; i < int(size); i++ { operands = append(operands, cpu.fetch()) } return operands }
1$ ϓϩάϥϜΧϯλ Y Y Y Y Y'' ʜ Y -%#
O Y" ʜ ϝϞϦ σίʔυ ΦϖϥϯυଈY" ͕Ϧʔυ͞ΕΔ
࣮ߦ func (cpu *CPU) Step() Cycle { opcode := cpu.fetch()
inst := instructions[opcode] operands := cpu.fetchOperands( inst.OperandsSize ) inst.Execute(cpu, operands) return inst.Cycles }
࣮ߦ // 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] }
#VT.FNPSZ
ϝϞϦϚοϓ 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''''
ϝϞϦϚοϓ 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'''' ΧʔτϦοδ෦ ͷϝϞϦ
Ϧʔυ 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 }
(16
MJOFQY ࠨ͔Βӈʹ ඳը͍ͯ͘͠ λΠϛϯά -$%
-$% λΠϛϯά MJOFQY ը໘ӈ·Ͱ౸ୡͨ͠Β ࣍ͷϥΠϯͷࠨʹΔ ʢ)CMBOL ͜ͷظؒΛؚΊΔͱ MJOFDMPDL
-$% λΠϛϯά MJOFQY MJOF
࣍ͷඳըͷͨΊʹઌ಄Δ ͜ͷͨΊͷظؒ7#MBOL MJOFͷ࣌ؒ -$% λΠϛϯά MJOFQY ϑϨʔϜ
DMPDL DMPDL
(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ͷجຊಈ࡞ 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αΠΫϧ
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 ΧϯτΞοϓ ඳըதϥΠϯΛࣔ͢ ϨδελΛΠϯΫϦϝϯτ
(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ߏங
(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ͷجຊಈ࡞ 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ظؒྃ
̔#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ͷ৭ใ
λΠϧσʔλ Y Y'' Y'' Y λΠϧ൪߸͕̌ʙ·Ͱ ׂΓͯΒΕΔ
λΠϧϚοϓ QY UJMF QY UJMF Y Y#'' YdY#''Λ YλΠϧͷ λΠϧ൪߸ͰຒΊΔ
QY UJMF QY UJMF දࣔՄೳൣғ දࣔൣғ
λΠϧϚοϓ 4DSPMM: Y'' 4DSPMM9 Y'' QY QY දࣔൣғ
σʔλͷՄࢹԽ IUUQTCPLVXFCHJUIVCJPHPQIFSCPZEFCVHIUNM
എܠͷඳը 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 } }
എܠͷඳը 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 } } ϥΠϯ
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:࠲ඪ
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".͔Βऔಘ λΠϧ൪߸͔Β ύϨοτ൪߸Λऔಘ
എܠͷϥΠϯඳը 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(#" Λ֨ೲ
ΤϛϡϨʔλ
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() } } } ΤϛϡϨʔλͷجຊಈ࡞
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
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ΛܦաΫϩοΫՔಇ
ΤϛϡϨʔλͷجຊಈ࡞ 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() } } } ੵࢉΫϩοΫ͕̍ϑϨʔϜ ͑ͨΒը૾σʔλΛฦ͢
func (g *GB) Start() { t := time.NewTicker(16 * time.Millisecond)
for { select { case <-t.C: buf := g.Next() g.win.Render(buf) } } } ΤϛϡϨʔλͷجຊಈ࡞ NTपظͰը૾Λੜ
ΤϛϡϨʔλͷجຊಈ࡞ 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() }
5FTUJOH
ΤϛϡϨʔλͷςετ
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… } }) ΤϛϡϨʔλͷςετ
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… } }) ΤϛϡϨʔλͷςετ ҙͷϑϨʔϜͷը૾Λ औಘͰ͖ΔΑ͏ʹ͓ͯ͘͠
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ͱͯ͠อଘ
ΤϛϡϨʔλͷςετ reg-cli ./test/actual ./test/expect ./test/diff -R ./report.html SFHWJ[SFHDMJΛͬͯ 7JTVBM3FHSFTTJPO5FTU
ΤϛϡϨʔλͷςετ ը૾ʹ͕ࠩ͋ͬͨΒ$*Λམͱ͢Α͏ʹ
8FC"TTFNCMZ
ɾϒϥβͰಈ͘όΠφϦϑΥʔϚοτ ɾ͍ΖΜͳݴޠ͔ΒXBTNΛు͚Δ ɾαΠζ͕খ͘͞ϩʔσΟϯά͍࣌ؒ 8FC"TTFNCMZ
ᶃ8BTNͷॳظԽ ᶄ+4ଆؔΛFYQPSU ᶅ+4Ͱ30.σʔλΛऔಘ ᶆ8BTNʹ30.σʔλΛ͠ ΤϛϡϨʔλ࡞ ᶇSFRVFTU"OJNBUJPO'SBNF ͰඳըσʔλΛऔಘ͠$BOWBTʹ ඳը ϒϥβͰಈ࡞ͤ͞Δઓུ
ϒϥβͰಈ࡞ͤ͞Δઓུ ᶃ8BTNͷॳظԽ ᶄ+4ଆؔΛFYQPSU ᶅ+4Ͱ30.σʔλΛऔಘ ᶆ8BTNʹ30.σʔλΛ͠ ΤϛϡϨʔλ࡞ ᶇSFRVFTU"OJNBUJPO'SBNF ͰඳըσʔλΛऔಘ͠$BOWBTʹ ඳը
8BTN <script src="wasm_exec.js"></script>
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
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ΦϒδΣΫτΛ͢
ᶃ8BTNͷॳظԽ ᶄ+4ଆؔΛFYQPSU ᶅ+4Ͱ30.σʔλΛऔಘ ᶆ8BTNʹ30.σʔλΛ͠ ΤϛϡϨʔλ࡞ ᶇSFRVFTU"OJNBUJPO'SBNF ͰඳըσʔλΛऔಘ͠$BOWBTʹ ඳը ϒϥβͰಈ࡞ͤ͞Δઓུ
func main() { w := js.Global() w.Set("GB", js.FuncOf(newGB)) select {}
} 8BTN(PMBOH HMPCBMʹ(#Λੜ͢
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͔ΒΧʔτϦοδͷ σʔλΛड͚औΔ
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
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
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ͱͯ͠ฦ͢
ᶃ8BTNͷॳظԽ ᶄ+4ଆؔΛFYQPSU ᶅ+4Ͱ30.σʔλΛऔಘ ᶆ8BTNʹ30.σʔλΛ͠ ΤϛϡϨʔλ࡞ ᶇSFRVFTU"OJNBUJPO'SBNF ͰඳըσʔλΛऔಘ͠$BOWBTʹ ඳը ϒϥβͰಈ࡞ͤ͞Δઓུ
const rom = await fetch("./tobu.gb"); const buf = await rom.arrayBuffer();
8BTN+4
ᶃ8BTNͷॳظԽ ᶄ+4ଆؔΛFYQPSU ᶅ+4Ͱ30.σʔλΛऔಘ ᶆ8BTNʹ30.σʔλΛ͠ ΤϛϡϨʔλ࡞ ᶇSFRVFTU"OJNBUJPO'SBNF ͰඳըσʔλΛऔಘ͠$BOWBTʹ ඳը ϒϥβͰಈ࡞ͤ͞Δઓུ
let gb = new GB(new Uint8Array(buf)); 8BTN+4
ᶃ8BTNͷॳظԽ ᶄ+4ଆؔΛFYQPSU ᶅ+4Ͱ30.σʔλΛऔಘ ᶆ8BTNʹ30.σʔλΛ͠ ΤϛϡϨʔλ࡞ ᶇSFRVFTU"OJNBUJPO'SBNF ͰඳըσʔλΛऔಘ͠$BOWBTʹ ඳը ϒϥβͰಈ࡞ͤ͞Δઓུ
const frame = () => { const img = gb.next();
image.data.set(img); ctx.putImageData(image, 0, 0); requestAnimationFrame(frame); }; frame(); 8BTN+4
GOOS=js GOARCH=wasm go build -tags=wasm -oɹ“main.wasm” "gopher-boy/wasm_main.go" Ϗϧυ
.# αΠζ wasm ,# wasm_exec.js
ύϑΥʔϚϯε 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
ύϑΥʔϚϯε 19.11msec
func (cpu *CPU) applyZeroBy(computed byte) { if computed == 0
{ cpu.setFlag(Z) } else { cpu.clearFlag(Z) } } BQQMZ;FSP#Z HP
8BTNXBU wasm2wat main.wasm -o main.wat
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)
͍͞͝ʹ ɾήʔϜϘʔΠ͕݁ߏγϯϓϧ ɹͳͷͰ͓͢͢Ί ɾ8BTNݱ࣌ͰαΠζ ɹύϑΥʔϚϯεʹߟྀ͕ඞཁ ɹ UJOZHPͳͲʹظ ɾࣗͬͯΈΑ͏͔ͱ ɹࢥ͍͚ͬͯͨͩͨΒ͍Ͱ͢
͋Γ͕ͱ͏͍͟͝·ͨ͠✨