Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Sign up for free
Menu
Search
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Pricing
Search
Sign in
Sign up for free
gocon2019.pdf
Search
bokuweb
October 28, 2019
Programming
14k
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.9k
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
[2026-09-26]空論ジェネリックプロセス~テスト資産とAIで紡ぐ、再現可能なパフォーマンスチューニングの話~
tosite
0
270
當我們開始不讀 Code - AI Agent 時代,Code Review 與 Clean Code 還剩下什麼?
jame2408
1
280
Augmenting AI with the Power of Jakarta EE
ivargrimstad
0
280
見えないものを探る要求要件定義に必要な基本的思考 / invisible-requirement-thinking
minodriven
15
8.6k
JIS keyboard support in Omarchy
lef237
0
150
機械に任せるテスト、人が見るテスト ⽣成AIで引き直した、運⽤保守フェーズの線引き
dske104
0
190
Herb in Rails 8.2: Your ERB views, now HTML-aware @ Rails World 2026, Austin, Texas
marcoroth
0
220
npm との違いを手がかりにGo の依存管理を理解したい
optim
0
1k
作れる、でも消せるか?──安全に「枯らして消す」機能削除術 / Drain, Then Delete
chobishiba
2
860
TechTown - Creating Composable Callables in Contemporary C++
rollbear
0
140
[Rails World 2026] Durable orchestration on Rails: from continuation to workflow
palkan
1
840
re:Inventに行く前に知っておきたい現地参加のノウハウ
nokomoro3
0
450
Featured
See All Featured
The Hidden Cost of Media on the Web [PixelPalooza 2025]
tammyeverts
2
530
Leading Effective Engineering Teams in the AI Era
addyosmani
9
2.8k
Unsuck your backbone
ammeep
672
58k
The Success of Rails: Ensuring Growth for the Next 100 Years
eileencodes
47
8.4k
Neural Spatial Audio Processing for Sound Field Analysis and Control
skoyamalab
0
570
Between Models and Reality
mayunak
4
500
New Earth Scene 8
popppiees
4
2.7k
What does AI have to do with Human Rights?
axbom
PRO
1
2.5k
Templates, Plugins, & Blocks: Oh My! Creating the theme that thinks of everything
marktimemedia
31
2.9k
How GitHub (no longer) Works
holman
316
150k
XXLCSS - How to scale CSS and keep your sanity
sugarenia
250
1.3M
Java REST API Framework Comparison - PWX 2021
mraible
34
9.8k
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ͳͲʹظ ɾࣗͬͯΈΑ͏͔ͱ ɹࢥ͍͚ͬͯͨͩͨΒ͍Ͱ͢
͋Γ͕ͱ͏͍͟͝·ͨ͠✨