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
Sponsored
·
Ship Features Fearlessly
Turn features on and off without deploys. Used by thousands of Ruby developers.
→
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.8k
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
Pythonの実行はどこまで賢くなったのか? CPythonとPyPyから見る最適化のしくみ
curekoshimizu
4
2.3k
Cloudflare is Agents
chimame
0
180
属人化した知識を、 AIが辿れる地図にする
pkshadeck
PRO
1
220
Go 1.27 における memory allocation の高速化
andpad
0
350
承認済みなのに差戻しできてしまうバグ、型で潰せます
shinchit
0
120
複数の Claude Code が"放置"されてしまう問題をCLI ダッシュボードを自作して解決した話
sumihiro3
1
740
React本体のコードリーディング
high_g_engineer
1
160
生成AIで帳票OCRが「簡単に」作れる時代になった?
kon_shou
0
1.2k
高専、大学編入、そして未踏へ〜プロダクト開発とキャリアの歩み - Technical College, University Transfer, and On to “Mitou” / My Journey in Product Development and Career
pkmiya
0
100
Dockerfile CMD for Node.js
grazie1999
0
140
go-spidermonkeyでAIエージェントのCode Modeを実装する
syumai
3
1.3k
AIの中の人になってみる
htkym
0
140
Featured
See All Featured
Bioeconomy Workshop: Dr. Julius Ecuru, Opportunities for a Bioeconomy in West Africa
akademiya2063
PRO
1
300
Paper Plane
katiecoart
PRO
2
53k
Context Engineering - Making Every Token Count
addyosmani
9
1.1k
Principles of Awesome APIs and How to Build Them.
keavy
128
18k
JavaScript: Past, Present, and Future - NDC Porto 2020
reverentgeek
52
6.1k
Color Theory Basics | Prateek | Gurzu
gurzu
0
440
Thoughts on Productivity
jonyablonski
76
5.3k
From π to Pie charts
rasagy
0
330
The agentic SEO stack - context over prompts
schlessera
0
880
The Success of Rails: Ensuring Growth for the Next 100 Years
eileencodes
47
8.3k
16th Malabo Montpellier Forum Presentation
akademiya2063
PRO
0
360
Evolving SEO for Evolving Search Engines
ryanjones
0
270
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ͳͲʹظ ɾࣗͬͯΈΑ͏͔ͱ ɹࢥ͍͚ͬͯͨͩͨΒ͍Ͱ͢
͋Γ͕ͱ͏͍͟͝·ͨ͠✨