読者です 読者をやめる 読者になる 読者になる

Let's write β

趣味で書いたこととか、RustとLispが好き

NasmでLifeGame

Assembler

更新が非常に空きました。久々にアセンブラです。

%include "stdio.inc"

section .bss
array0:  resb 8
array1:  resb 8
flag:    resb 1

section .text
global start

start:
  mov   rcx,flag
  mov   byte [rcx],0
  mov   rax,1
  mov   rbx,0
  mov   rcx,1
  call  setCell
  mov   rax,2
  mov   rbx,1
  mov   rcx,1
  call  setCell
  mov   rax,0
  mov   rbx,2
  mov   rcx,1
  call  setCell
  mov   rax,1
  mov   rbx,2
  mov   rcx,1
  call  setCell
  mov   rax,2
  mov   rbx,2
  mov   rcx,1
  call  setCell
  call  showMem
  call  NewLine

  call  updateWorld
  call  showMem
  call  NewLine
  call  updateWorld
  call  showMem
  call  NewLine
  call  Exit

toggle:
  push  rax
  push  rcx
  mov   rcx,flag
  mov   rax,[rcx]
  test  rax,rax
  jz    .PL0
  mov   byte [rcx],0
  jmp   .end
.PL0:
  mov   byte [rcx],1
.end:
  pop   rcx
  pop   rax
  ret
;------
; set mem
; rax <- idx
; rbx <- val
;------
setMem:
  push rdx
  xor   rdx,rdx
  mov   rdx,flag
  mov   rdx,[rdx]
  test   rdx,rdx
  jz    .PL0
  mov   rdx,array0
  jmp   .body
.PL0:
  mov   rdx,array1
.body:
  push  rcx
  mov   rcx,rax
  mov   rax,1
  shl   rax,cl
  pop   rcx
  test  rbx,rbx
  jz    .off
  or    [rdx],rax
  jmp   .end
.off:
  not   rax
  and   [rdx],rax 
.end:
  pop  rdx
  ret

;------
; getMem
; rax <- idx => rax <- val
;-------
getMem:
  push  rdx
  mov   rdx,flag
  mov   rdx,[rdx]
  test   rdx,rdx
  jz    .PL0
  mov   rdx,array0
  jmp   .body
.PL0:
  mov   rdx,array1
.body:
  mov   rdx,[rdx]
  push  rcx
  mov   rcx,rax
  mov   rax,1
  shl   rax,cl
  and   rdx,rax
  shr   rdx,cl
  pop   rcx
  mov   rax,rdx
  pop   rdx
  ret

showMem:
  push  rcx
  push  rsi
  mov   rcx,64
  mov   rsi,8
.loop0:
  dec   rcx
  mov   rax,rcx
  call  getMem
  add   rax,'0'
  call  OutChar
  mov   rax,rcx
  xor   rdx,rdx
  div   rsi
  test  rdx,rdx
  jnz   .loop1
  call NewLine
.loop1:
  test  rcx,rcx
  jnz   .loop0
  pop   rsi
  pop   rcx
  ret

;---------
; getCellIdx
; rax <- x, rbx <- y
; => rax <- idx
;----------
getCellIdx:
  test rax,rax
  jns  .PL0
  add  rax,8
.PL0:
  cmp  rax,8
  jl  .PL1
  push  rdx
  push  rdi
  mov   rdi,8
  xor   rdx,rdx
  div   rdi
  mov   rax,rdx
  pop   rdi
  pop   rdx
.PL1:
  test rbx,rbx
  jns   .PL2
  add  rbx,8
.PL2:
  cmp   rbx,8
  jl   .PL3
  push  rdx
  push  rdi
  push  rax
  mov   rdi,8
  mov   rax,rbx
  div   rdi
  mov   rbx,rdx
  pop   rax
  pop   rdi
  pop   rdx
.PL3:
  push rcx
  mov rcx,rax
  mov rax,8
  mul rbx
  add rax,rcx
  pop rcx
  ret
  
;-------
; getCell
; rax <- x
; rbx <- y
; => rax <- data
;--------
getCell:
  push  rbx
  call getCellIdx
  pop   rbx
  call getMem
  ret

;-----------
; Set Cell
; rax <- x, rbx <- y, cl <- val
;-----------
setCell:
  call getCellIdx
  push  rbx
  mov   rbx,rcx
  call setMem
  pop   rbx
  ret

;----------
; Get Living Neighbor cell count
; rax <- x
; rbx <- y
; => rax <- count 
;---------
getNeighbor:
  push rcx
  push rdi
  push rsi
  mov  rdi,-1
  xor rcx,rcx
.loop0:
  cmp  rdi,2
  jge  .end0
  mov  rsi,-1 
.loop1:
  cmp rsi,2
  jge .end1
  cmp rsi,0
  jne .body
  cmp rdi,0
  jne .body
  jmp .bodyend
.body:
  add rax,rdi
  add rbx,rsi
  push rax
  call getCell
  add rcx,rax
  pop rax
  sub rax,rdi
  sub rbx,rsi
.bodyend:
  inc rsi
  jmp .loop1
.end1:
  inc  rdi
  jmp  .loop0
.end0:
  pop rsi
  pop rdi
  mov rax,rcx
  pop rcx
  ret 

;-------
; Update Cell
; rax <- x, rbx <- y
; update cell
;-------
updateCell:
  push  rcx
  mov   rcx,rax
  push  rax
  call  getCell
  test  rax,rax
  jz    .dead
  mov   rax,rcx
  call  getNeighbor
  cmp   rax,2
  jz  .born
  cmp  rax,3
  jz  .born
  jmp  .death
.dead:
  mov   rax,rcx
  call  getNeighbor
  cmp   rax,3
  jz    .born
  jmp   .death
.born:
  mov   rax,rcx
  push rcx
  xor   rcx,rcx
  mov   rcx,1
  call  toggle
  call setCell
  call  toggle
  pop   rcx
  jmp .end
.death:
  mov   rax,rcx
  push rcx
  xor   rcx,rcx
  call  toggle
  call setCell
  call  toggle
  pop rcx
.end:
  pop   rax
  pop   rcx
  ret

updateWorld:
  push  rax
  push  rbx
  xor   rax,rax
  mov   rax,7
.loop0:
  test  rax,rax
  js   .end0
  xor   rbx,rbx
  mov   rbx,7
.loop1:
  test  rbx,rbx
  js    .end1
  call  updateCell
  dec   rbx
  jmp   .loop1
.end1:
  dec   rax
  jmp   .loop0
.end0:
  pop   rbx
  pop   rax
  call  toggle
  ret

cleanWorld:
  push  rax
  push  rbx
  xor   rax,rax
  mov   rax,7
.loop0:
  test  rax,rax
  js   .end0
  xor   rbx,rbx
  mov   rbx,7
.loop1:
  test  rbx,rbx
  js    .end1
  push  rcx
  xor   rcx,rcx
  call  setCell
  pop   rcx
  dec   rbx
  jmp   .loop1
.end1:
  dec   rax
  jmp   .loop0
.end0:
  pop   rbx
  pop   rax
  call  toggle
  ret

最初は1つのセルを表現するのに、1byteつかうという豪勢なプログラムを書いていたのですが、本来ならば1bitのみでセルの2状態が表現できるので、64マスならば64bitのみで表現できるはずなので、そのように書きなおしました。
64bitサイズだとぴったりレジスタのサイズと同じなので、マップ全体を取得してbit演算するのみセルの状態が取得できます。

64bit分のサイズの値をメモリから読み書きするために毎回伝送するのがどのくらいのコストなのだろうかという事が気になっています。行で取得してから列の分だけシフトした値と演算するようにすれば、伝送する分は8bitで済むので、こちらのほうが速いのだろうかとか考えています。

僕が働いているAzit.incでは一緒に働けるエンジニアを募集しています!
採用情報 — 株式会社アジット|Azit Inc.