Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
gocon2019.pdf
Search
bokuweb
October 28, 2019
Programming
13k
5
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
gocon2019.pdf
bokuweb
October 28, 2019
More Decks by bokuweb
See All by bokuweb
1000 行で実現する Linux on Browser
bokuweb
17
9.7k
emscripten_night_7.pdf
bokuweb
1
1.6k
ファミコンエミュレータの創り方
bokuweb
58
46k
component test
bokuweb
7
5.2k
Other Decks in Programming
See All in Programming
属人化した知識を、 AIが辿れる地図にする
pkshadeck
PRO
1
140
ルールを書いて終わらせないハーネスエンジニアリング
yug1224
4
1.9k
「人を評価する AI」の設計と実装
ryoyanara
0
160
Japan Community Day at Kubecon + CloudNativeCon Japan 2026: Learning Container Privilege Control by Building My Own Low-Level Container Runtime
ternbusty
1
140
AI時代に設計が 最大の生産性レバーになる 意図駆動開発とデータを消さない設計|Don't Delete Your Data or Your Intent — Design as the Deepest Lever in the AI Era
tomohisa
1
700
数百円から始めるRuby電子工作
tarosay
0
140
freee が目指す データ マネジメント戦略 AI-Ready 時代を支える 攻めのガバナンスとは
freee
PRO
0
280
その節約、円になってますか?
isamumumu
1
700
Claude Code全社展開のためにやったことn選~プラグイン302個・コミッター271人を支えるために~
kenchan
5
1.4k
Augmenting AI with the Power of Jakarta EE
ivargrimstad
0
520
今さら聞けない .NET CLI
htkym
0
180
【SRE NEXT 2026 Lunch Session】一人目専任SREの立ち上げを加速する ― AIと進めたオンボーディングで2分を0.04秒にした話
pkshadeck
PRO
0
3.5k
Featured
See All Featured
Art, The Web, and Tiny UX
lynnandtonic
304
22k
Beyond borders and beyond the search box: How to win the global "messy middle" with AI-driven SEO
davidcarrasco
3
200
Heart Work Chapter 1 - Part 1
lfama
PRO
8
36k
How STYLIGHT went responsive
nonsquared
100
6.2k
It's Worth the Effort
3n
188
29k
Producing Creativity
orderedlist
PRO
348
40k
A Guide to Academic Writing Using Generative AI - A Workshop
ks91
PRO
1
360
B2B Lead Gen: Tactics, Traps & Triumph
marketingsoph
0
190
Site-Speed That Sticks
csswizardry
13
1.4k
CSS Pre-Processors: Stylus, Less & Sass
bermonpainter
360
30k
Build your cross-platform service in a week with App Engine
jlugia
234
19k
sira's awesome portfolio website redesign presentation
elsirapls
0
320
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ͳͲʹظ ɾࣗͬͯΈΑ͏͔ͱ ɹࢥ͍͚ͬͯͨͩͨΒ͍Ͱ͢
͋Γ͕ͱ͏͍͟͝·ͨ͠✨