├── 8queens.asm ├── Makefile ├── README.md ├── os_dependent_stuff.asm └── winprinter.asm /8queens.asm: -------------------------------------------------------------------------------- 1 | %include "os_dependent_stuff.asm" 2 | mov r10, 0b11111111 ; all eight possibilities available 3 | mov r8, 0x000000000000 ; no squares under attack from anywhere 4 | movq xmm1, r8 ; maintain this state in xmm1 5 | mov r15, 0x000100010001 ; attack mask for one queen (left, right, and center) 6 | mov r14, 0xff ; mask for low byte 7 | movq xmm7, r14 ; stored in xmm register 8 | mov r13, rsp ; current stack pointer (if we backtrack here, then 9 | mov r14, rsp ; the entire solution space has been explored) 10 | sub r14, 2*8*7 ; this is where the stack pointer would be when we've 11 | ; completed a winning state 12 | %ifndef NOPRETTY 13 | mov rdi, 1 ; stdout 14 | mov rcx, 0 15 | movq xmm8, rcx ; bitmap of queens 16 | movq mm0, rcx ; horizontal position of printer 17 | %endif 18 | next_state: 19 | bsf rcx, r10 ; find next available position in current level 20 | jz backtrack ; if there is no available position, we must go back 21 | btc r10, rcx ; mark position as unavailable 22 | %ifndef NOPRETTY 23 | xor eax, eax 24 | bts eax, ecx 25 | pinsrb xmm8, eax, 0 ; insert mask for this queen 26 | %endif 27 | cmp rsp, r14 ; check if we've done 7 levels already 28 | je win ; if so, we have a win state. otherwise continue 29 | movq rax, xmm1 ; save current state ... 30 | push r10 31 | push rax ; ... to stack 32 | %ifndef NOPRETTY 33 | pslldq xmm8, 1 ; shift bitmap to the left 34 | %endif 35 | mov rax, r15 ; set up attack mask 36 | shl rax, cl ; shift into position 37 | movq xmm2, rax 38 | por xmm1, xmm2 ; mark as attacking in all directions 39 | vpsllw xmm2, xmm1, 1 ; shift entire state to left, place in xmm2 40 | vpsrlw xmm3, xmm1, 1 ; shift entire state to right, place in xmm3 41 | pblendw xmm1, xmm2, 0b100 ; only copy "left-attacking" word back from xmm2 42 | pblendw xmm1, xmm3, 0b010 ; only copy "right-attacking" word back from xmm3 43 | vpsrldq xmm2, xmm1, 4 ; shift state right 4 *bytes*, place in xmm2 44 | vpsrldq xmm3, xmm1, 2 ; shift state right 2 bytes, place in xmm3 45 | por xmm2, xmm3 ; collect bitwise ors in xmm2 46 | por xmm2, xmm1 47 | vpandn xmm4, xmm2, xmm7 ; invert and select low byte 48 | movq r10, xmm4 ; place in r10 49 | jmp next_state ; now we're set up to iterate 50 | 51 | backtrack: 52 | cmp rsp, r13 ; are we done? 53 | je done 54 | pop rcx ; restore last state 55 | pop r10 56 | movq xmm1, rcx 57 | %ifndef NOPRETTY 58 | psrldq xmm8, 1 ; shift bitmap to the right 59 | %endif 60 | jmp next_state ; try again 61 | 62 | win: 63 | inc r8 ; increment solution counter 64 | %ifndef NOPRETTY 65 | %include "winprinter.asm" 66 | %endif 67 | jmp next_state ; keep going 68 | 69 | done: 70 | %ifndef NOPRETTY 71 | mov rax, SYSCALL_WRITE 72 | mov rsi, cursormove2 73 | mov rdx, boardend - cursormove2 74 | syscall 75 | %endif 76 | mov rax, SYSCALL_EXIT ; set system call to exit 77 | mov rdi, r8 ; set system call argument to solution count 78 | syscall ; this will exit with our solution count as status 79 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # Detect OS. 2 | UNAME := $(shell uname) 3 | ifeq ($(UNAME),Darwin) 4 | FORMAT := macho64 5 | endif 6 | ifeq ($(UNAME),Linux) 7 | FORMAT := elf64 8 | endif 9 | 10 | .PHONY: run 11 | run: 8queens 12 | ./8queens; echo $$? 13 | 14 | 8queens: 8queens.o 15 | ld -o $@ $^ 16 | @echo '===' 17 | 18 | ifeq ($(NOPRETTY), 1) 19 | FLAGS += -DNOPRETTY 20 | endif 21 | 22 | -include *.dep 23 | %.o: ./nasm %.asm 24 | ./nasm $*.asm $(FLAGS) -f $(FORMAT) -o $@ -MD $*.dep 25 | 26 | .PHONY: distclean clean 27 | clean: 28 | rm -f *.o 8queens 29 | 30 | distclean: clean 31 | rm -rf download nasm *.dep 32 | 33 | #------------------------------------------------------------------------------- 34 | # Download NASM. 35 | NASM_DL_VERSION := 2.11 36 | download: ; mkdir download 37 | 38 | ifeq ($(UNAME),Darwin) 39 | # local filename to save 40 | NASM_DL := nasm-osx.zip 41 | 42 | # intermediate component of NASM binary URL 43 | NASM_DL_PLATFORM := macosx 44 | 45 | # final component of NASM binary URL 46 | NASM_DL_EXT := -macosx.zip 47 | 48 | # git hash-object 49 | NASM_DL_HASH := dae69c310bedc02f07501adef71795d46e8c2a18 50 | 51 | # archive to extract binary from (distinct from NASM_DL in the case of RPM) 52 | NASM_DL_ARCHIVE := nasm-osx.zip 53 | 54 | # binary to extract from archive 55 | NASM_DL_BIN = nasm-$(NASM_DL_VERSION)/nasm 56 | endif 57 | ifeq ($(UNAME),Linux) 58 | NASM_DL := nasm-linux.rpm 59 | NASM_DL_PLATFORM := linux 60 | NASM_DL_EXT := -1.x86_64.rpm 61 | NASM_DL_HASH := 2bc231565485b9c41d7c217ffe0e26a9ed9e7635 62 | NASM_DL_ARCHIVE := nasm-linux.cpio # generated from the RPM, see below 63 | NASM_DL_BIN := ./usr/bin/nasm 64 | endif 65 | 66 | download/$(NASM_DL): download 67 | curl "http://www.nasm.us/pub/nasm/releasebuilds/$(NASM_DL_VERSION)\ 68 | /$(NASM_DL_PLATFORM)/nasm-$(NASM_DL_VERSION)$(NASM_DL_EXT)" -o $@ 69 | test `git hash-object $@` = $(NASM_DL_HASH) 70 | 71 | # The Linux binary version of NASM is distributed as an RPM. 72 | # This is moderately annoying, but we can deal with it without adding any 73 | # superfluous dependencies. 74 | # The following code is adapted from: 75 | # http://rpm5.org/cvs/fileview?f=rpm/scripts/rpm2cpio&v=1.6 76 | %.cpio: %.rpm 77 | if test "$<" != "download/$(notdir $<)" ;\ 78 | then mv $< download/$(notdir $<); fi 79 | @echo "Generating $@ from download/$(notdir $<)..." 80 | @cd download; f=$(notdir $<); l=96; o=`expr $$l + 8`; set `od -j $$o -N 8 -t u1 $$f`; il=`expr 256 \* \( 256 \* \( 256 \* $$2 + $$3 \) + $$4 \) + $$5`; dl=`expr 256 \* \( 256 \* \( 256 \* $$6 + $$7 \) + $$8 \) + $$9`; z=`expr 8 + 16 \* $$il + $$dl`; o=`expr $$o + $$z + \( 8 - \( $$z \% 8 \) \) \% 8 + 8`; set `od -j $$o -N 8 -t u1 $$f`; il=`expr 256 \* \( 256 \* \( 256 \* $$2 + $$3 \) + $$4 \) + $$5`; dl=`expr 256 \* \( 256 \* \( 256 \* $$6 + $$7 \) + $$8 \) + $$9`; h=`expr 8 + 16 \* $$il + $$dl`; o=`expr $$o + $$h`; e="dd if=$$f ibs=$$o skip=1"; c=`($$e |file -) 2>/dev/null`; if echo $$c | grep -q gzip; then d=gunzip; elif echo $$c | grep -q bzip2; then d=bunzip2; elif echo $$c | grep -q xz; then d=unxz; elif echo $$c | grep -q cpio; then d=cat; else d=`which unlzma 2>/dev/null`; case "$$d" in /*) ;; *) d=`which lzmash 2>/dev/null`; case "$$d" in /*) d="lzmash -d -c" ;; *) d=cat ;; esac ;; esac; fi; $$e 2>/dev/null | $$d > $(notdir $@) 81 | 82 | ./nasm: download/$(NASM_DL_ARCHIVE) 83 | cd download \ 84 | && cpio -id --quiet $(NASM_DL_BIN) < $(NASM_DL_ARCHIVE) \ 85 | && mv $(NASM_DL_BIN) ../nasm \ 86 | && rmdir -p $(dir $(subst ./,,$(NASM_DL_BIN))) 87 | ls -l nasm 88 | ./nasm -v 89 | #------------------------------------------------------------------------------- 90 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 8queens 2 | ======= 3 | 4 | x64 assembler solution to n-queens problem. Especially if `n` happens to be 8. 5 | 6 | ``` 7 | bash-3.2$ git clone https://github.com/davidad/8queens.git 8 | Cloning into '8queens'... 9 | ``` 10 | ![pretty printing](http://i.imgur.com/ViRqInZ.png?1) 11 | 12 | Somewhat surprisingly, the very first time I tried to run this code, it successfully returned 92. 13 | (Though it did take me about 3 hours of thinking and plenty of referencing the Intel 64 manual 14 | before I could write it at all.) It took another 4 or 5 hours to get the pretty printing this 15 | pretty. 16 | -------------------------------------------------------------------------------- /os_dependent_stuff.asm: -------------------------------------------------------------------------------- 1 | %ifidn __OUTPUT_FORMAT__,elf64 2 | %define SYSCALL_WRITE 1 3 | %define SYSCALL_EXIT 60 4 | %elifidn __OUTPUT_FORMAT__,macho64 5 | %define SYSCALL_WRITE 0x2000004 6 | %define SYSCALL_EXIT 0x2000001 7 | %endif 8 | 9 | default rel 10 | section .text 11 | global _start 12 | _start: ; OSX likes "_start" 13 | global start 14 | start: ; Linux likes "start" 15 | -------------------------------------------------------------------------------- /winprinter.asm: -------------------------------------------------------------------------------- 1 | section .data 2 | 3 | cursormove0: 4 | db `\n\n\n\n\n\n\n\n\n\n\e[10A` 5 | 6 | boardstart: 7 | 8 | ; board top 9 | db `\u2553` 10 | times 8 db `\u2500` 11 | db `\u2556` 12 | 13 | %define evenrow `\e[1;31;47m \e[m\e[1;31;40m \e[m` 14 | %define oddrow `\e[1;31;40m \e[m\e[1;31;47m \e[m` 15 | %define rowend `\e[1B\e[10D` 16 | 17 | db rowend 18 | 19 | %rep 4 20 | db `\u2551` 21 | times 4 db evenrow 22 | db `\u2551` 23 | db rowend 24 | 25 | db `\u2551` 26 | times 4 db oddrow 27 | db `\u2551` 28 | db rowend 29 | %endrep 30 | 31 | boardbottom: 32 | db `\u2559` 33 | times 8 db `\u2500` 34 | db `\u255c` 35 | db rowend 36 | 37 | cursormove1: 38 | db `\e[10A\e[10C` 39 | cursormove2: 40 | db `\e[10B\e[80D` 41 | boardend:db 0 42 | boardlen0 equ cursormove2 - cursormove0 43 | boardlen1 equ cursormove2 - boardstart 44 | boardlen2 equ boardend - boardstart 45 | dq boardlen1, boardlen2 46 | firstsquare equ boardstart+0x34 47 | colstride equ 0x0e 48 | rowstride equ 0x7f 49 | empty_byte: db ' ' 50 | queen_byte: db 'Q' 51 | 52 | section .text 53 | mov r12, firstsquare 54 | movq r9, xmm8 ; fetch queen bitmap 55 | mov rbx, empty_byte 56 | xor rax, rax ; clear rax 57 | xor r11, r11 ; bit index = 0 58 | format_loop: ; unrolled loop 59 | %assign i 0 ; these % directives are handled by nasm's preprocessor 60 | %rep 8 61 | bt r9, r11 62 | setc al 63 | xlatb 64 | mov byte [r12+colstride*i], al 65 | inc r11 66 | %assign i i+1 67 | %endrep 68 | add r12, rowstride 69 | cmp r11, 8*8 70 | jl format_loop 71 | 72 | mov rsi, boardstart 73 | movq r11, mm0 74 | inc r11 75 | mov rdx, boardlen1 76 | cmp r11, 8 77 | jne check_one 78 | mov rdx, boardlen2 79 | mov r11, 0 80 | jmp do_print 81 | check_one: 82 | cmp r11, 1 83 | jne do_print 84 | mov rsi, cursormove0 85 | mov rdx, boardlen0 86 | 87 | do_print: 88 | movq mm0, r11 89 | mov rax, SYSCALL_WRITE 90 | syscall 91 | --------------------------------------------------------------------------------