mirror of
https://github.com/MoleskiCoder/EightBitNet.git
synced 2026-10-23 23:02:16 +00:00
z80 sudoku
This commit is contained in:
1 parent
5a670ba718
commit
ade1ed9ae7
6 files changed
+556
-1
No files matched your search
@@ -20,6 +20,8 @@ namespace Z80.Test
|
||||
|
||||
public string RomDirectory { get; } = "roms";
|
||||
|
||||
public string Program { get; } = "zexall.com";
|
||||
//public string Program { get; } = "zexall.com";
|
||||
|
||||
public string Program { get; } = "z80_sudoku/sudoku.com";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
# z80_sudoku
|
||||
|
||||
Sudoku solver in Z80 assembler, assembled with
|
||||
[sjasmplus](https://github.com/z00m128/sjasmplus) and packaged as a CP/M `.COM`.
|
||||
|
||||
A port of the 6502 version (`6502_sudoku`). It uses the same recursive
|
||||
backtracking algorithm with the per-candidate row/column/box scan. (On 8-bit
|
||||
CPUs the naive scan beats bitmask/MRV variants - that was measured on the
|
||||
6502 - so this port keeps the scan.)
|
||||
|
||||
## Building
|
||||
|
||||
make # or: sjasmplus sudoku.asm
|
||||
|
||||
Produces `sudoku.com` (CP/M binary) and `sudoku.hex` (Intel HEX, load address $0100) via the `SAVEBIN`/`SAVEHEX` directives.
|
||||
|
||||
## Running
|
||||
|
||||
It's a standard CP/M transient program (`ORG $0100`). Run it under CP/M or a
|
||||
CP/M emulator; it prints via BDOS function 2 and returns via warm boot:
|
||||
|
||||
Solving puzzle: pass
|
||||
|
||||
followed by the solved grid.
|
||||
|
||||
## Notes
|
||||
|
||||
- Console output: CP/M BDOS function 2 (`LD E,char / LD C,2 / CALL 5`).
|
||||
- The board and tables live in the TPA (RAM), so the puzzle is mutated in place.
|
||||
- `n` (the cell index) is passed to `solve` in `C`; recursion uses the hardware
|
||||
stack. `is_available` returns its result in the carry flag and preserves
|
||||
`A`, `B`, `C`.
|
||||
- The row, column and box scans are fully unrolled (as in the 6502/6809 ports).
|
||||
@@ -0,0 +1,15 @@
|
||||
# Makefile for the Z80 (CP/M) sudoku port - sjasmplus
|
||||
#
|
||||
# sjasmplus writes sudoku.com via the SAVEBIN directive at the end of the
|
||||
# source. Run the resulting .COM under CP/M (or a CP/M emulator).
|
||||
|
||||
ASM = sjasmplus
|
||||
|
||||
all: sudoku.com
|
||||
|
||||
sudoku.com: sudoku.asm
|
||||
$(ASM) sudoku.asm
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
rm -fv *.com *.hex *.lst *.map *.sym
|
||||
@@ -0,0 +1,441 @@
|
||||
; ============================================================================
|
||||
; z80_sudoku - Sudoku solver in Z80 assembler (sjasmplus), CP/M .COM
|
||||
;
|
||||
; A port of the 6502 version. Same recursive backtracking algorithm with the
|
||||
; per-candidate row/column/box scan (the naive scan is the right choice on an
|
||||
; 8-bit CPU - bitmask/MRV variants were measured slower on the 6502).
|
||||
;
|
||||
; Console output uses CP/M BDOS function 2; the program returns via warm boot.
|
||||
; ============================================================================
|
||||
|
||||
DEVICE NOSLOT64K
|
||||
|
||||
BDOS equ 0x0005
|
||||
CONOUT equ 2
|
||||
|
||||
ORG 0x0100
|
||||
|
||||
start:
|
||||
ld sp, stacktop
|
||||
|
||||
ld hl, msg_solving
|
||||
call print_string
|
||||
|
||||
ld c, 0 ; n = 0
|
||||
call solve
|
||||
or a
|
||||
jr nz, sv_fail
|
||||
|
||||
ld hl, msg_pass
|
||||
call print_string
|
||||
call print_board
|
||||
jr sv_done
|
||||
sv_fail:
|
||||
ld hl, msg_fail
|
||||
call print_string
|
||||
sv_done:
|
||||
ld a, 0x0a ; trailing newline
|
||||
call conout
|
||||
jp 0 ; warm boot back to CP/M
|
||||
|
||||
; ----------------------------------------------------------------------------
|
||||
; conout - print the character in A via BDOS function 2. Preserves all regs.
|
||||
; ----------------------------------------------------------------------------
|
||||
conout:
|
||||
push af
|
||||
push bc
|
||||
push de
|
||||
push hl
|
||||
ld e, a
|
||||
ld c, CONOUT
|
||||
call BDOS
|
||||
pop hl
|
||||
pop de
|
||||
pop bc
|
||||
pop af
|
||||
ret
|
||||
|
||||
; print_string - print the nul-terminated string at HL.
|
||||
print_string:
|
||||
push af
|
||||
ps_loop:
|
||||
ld a, (hl)
|
||||
or a
|
||||
jr z, ps_done
|
||||
call conout
|
||||
inc hl
|
||||
jr ps_loop
|
||||
ps_done:
|
||||
pop af
|
||||
ret
|
||||
|
||||
; ----------------------------------------------------------------------------
|
||||
; solve - recursive backtracking
|
||||
; entry: C = cell index n (0..81)
|
||||
; exit : A = 0 on success, A <> 0 on failure
|
||||
; ----------------------------------------------------------------------------
|
||||
solve:
|
||||
ld a, c
|
||||
cp 81
|
||||
jr nz, sv_go
|
||||
xor a ; reached the end -> solved
|
||||
ret
|
||||
sv_go:
|
||||
ld hl, puzzle
|
||||
ld b, 0
|
||||
add hl, bc ; HL = puzzle + n
|
||||
ld a, (hl)
|
||||
or a
|
||||
jr z, sv_empty
|
||||
inc c ; already assigned -> skip
|
||||
jp solve ; tail recursion
|
||||
sv_empty:
|
||||
ld b, 1 ; first candidate digit
|
||||
sv_loop:
|
||||
ld a, b
|
||||
call is_available ; CY = 1 if the digit is already used
|
||||
jr c, sv_next
|
||||
ld a, b
|
||||
ld hl, puzzle
|
||||
ld d, 0
|
||||
ld e, c
|
||||
add hl, de ; HL = puzzle + n
|
||||
ld (hl), a ; tentative assignment
|
||||
push bc ; save digit (B) and n (C)
|
||||
inc c
|
||||
call solve
|
||||
pop bc
|
||||
or a
|
||||
jr z, sv_success
|
||||
sv_next:
|
||||
inc b
|
||||
ld a, b
|
||||
cp 10
|
||||
jp nz, sv_loop
|
||||
ld hl, puzzle ; every digit failed -> unmake and backtrack
|
||||
ld d, 0
|
||||
ld e, c
|
||||
add hl, de
|
||||
ld (hl), 0
|
||||
ld a, 1 ; non-zero -> failure
|
||||
ret
|
||||
sv_success:
|
||||
xor a
|
||||
ret
|
||||
|
||||
; ----------------------------------------------------------------------------
|
||||
; is_available - may digit A be placed in cell C (n)?
|
||||
; entry: A = digit, C = n
|
||||
; exit : CY = 1 if the digit is already used (not available), CY = 0 if free
|
||||
; preserves A, B, C.
|
||||
; ----------------------------------------------------------------------------
|
||||
is_available:
|
||||
push bc
|
||||
; --- row (9 contiguous cells from the row start) ---
|
||||
ld hl, table_move2row_start
|
||||
ld b, 0
|
||||
add hl, bc
|
||||
ld e, (hl)
|
||||
ld hl, puzzle
|
||||
ld d, 0
|
||||
add hl, de ; HL = puzzle + row start
|
||||
cp (hl)
|
||||
jr z, ia_r
|
||||
inc hl
|
||||
cp (hl)
|
||||
jr z, ia_r
|
||||
inc hl
|
||||
cp (hl)
|
||||
jr z, ia_r
|
||||
inc hl
|
||||
cp (hl)
|
||||
jr z, ia_r
|
||||
inc hl
|
||||
cp (hl)
|
||||
jr z, ia_r
|
||||
inc hl
|
||||
cp (hl)
|
||||
jr z, ia_r
|
||||
inc hl
|
||||
cp (hl)
|
||||
jr z, ia_r
|
||||
inc hl
|
||||
cp (hl)
|
||||
jr z, ia_r
|
||||
inc hl
|
||||
cp (hl)
|
||||
jr z, ia_r
|
||||
jr ia_row_ok ; no conflict in row
|
||||
ia_r: jp ia_used ; (trampoline: ia_used is > 127 bytes away)
|
||||
ia_row_ok:
|
||||
; --- column (9 cells, stride 9) ---
|
||||
ld hl, table_move2x
|
||||
ld b, 0
|
||||
add hl, bc
|
||||
ld e, (hl)
|
||||
ld hl, puzzle
|
||||
ld d, 0
|
||||
add hl, de ; HL = puzzle + column
|
||||
ld de, 9
|
||||
cp (hl)
|
||||
jr z, ia_c
|
||||
add hl, de
|
||||
cp (hl)
|
||||
jr z, ia_c
|
||||
add hl, de
|
||||
cp (hl)
|
||||
jr z, ia_c
|
||||
add hl, de
|
||||
cp (hl)
|
||||
jr z, ia_c
|
||||
add hl, de
|
||||
cp (hl)
|
||||
jr z, ia_c
|
||||
add hl, de
|
||||
cp (hl)
|
||||
jr z, ia_c
|
||||
add hl, de
|
||||
cp (hl)
|
||||
jr z, ia_c
|
||||
add hl, de
|
||||
cp (hl)
|
||||
jr z, ia_c
|
||||
add hl, de
|
||||
cp (hl)
|
||||
jr z, ia_c
|
||||
jr ia_col_ok ; no conflict in column
|
||||
ia_c: jp ia_used
|
||||
ia_col_ok:
|
||||
; --- box (offsets 0,1,2,9,10,11,18,19,20); ia_used now in jr range ---
|
||||
ld hl, table_move2box_start
|
||||
ld b, 0
|
||||
add hl, bc
|
||||
ld e, (hl)
|
||||
ld hl, puzzle
|
||||
ld d, 0
|
||||
add hl, de ; HL = puzzle + box start
|
||||
cp (hl)
|
||||
jr z, ia_used
|
||||
inc hl
|
||||
cp (hl)
|
||||
jr z, ia_used
|
||||
inc hl
|
||||
cp (hl)
|
||||
jr z, ia_used
|
||||
ld de, 7
|
||||
add hl, de
|
||||
cp (hl)
|
||||
jr z, ia_used
|
||||
inc hl
|
||||
cp (hl)
|
||||
jr z, ia_used
|
||||
inc hl
|
||||
cp (hl)
|
||||
jr z, ia_used
|
||||
ld de, 7
|
||||
add hl, de
|
||||
cp (hl)
|
||||
jr z, ia_used
|
||||
inc hl
|
||||
cp (hl)
|
||||
jr z, ia_used
|
||||
inc hl
|
||||
cp (hl)
|
||||
jr z, ia_used
|
||||
pop bc ; available
|
||||
or a ; CY = 0
|
||||
ret
|
||||
ia_used:
|
||||
pop bc
|
||||
scf ; CY = 1
|
||||
ret
|
||||
|
||||
; ----------------------------------------------------------------------------
|
||||
; Board printing
|
||||
; ----------------------------------------------------------------------------
|
||||
print_element: ; entry: A = cell index
|
||||
push hl
|
||||
push bc
|
||||
ld c, a ; C = index
|
||||
ld a, 0x20 ; space
|
||||
call conout
|
||||
ld hl, puzzle
|
||||
ld b, 0
|
||||
add hl, bc ; HL = puzzle + index
|
||||
ld a, (hl)
|
||||
or a
|
||||
jr z, pe_blank
|
||||
add a, 0x30 ; digit -> ASCII
|
||||
call conout
|
||||
jr pe_fin
|
||||
pe_blank:
|
||||
ld a, 0x2d ; '-'
|
||||
call conout
|
||||
pe_fin:
|
||||
ld a, 0x20 ; space
|
||||
call conout
|
||||
pop bc
|
||||
pop hl
|
||||
ret
|
||||
|
||||
print_box_break_vertical:
|
||||
ld a, 0x7c ; '|'
|
||||
call conout
|
||||
ret
|
||||
|
||||
print_box_break_horizontal:
|
||||
ld hl, msg_hbreak
|
||||
call print_string
|
||||
ret
|
||||
|
||||
print_newline:
|
||||
ld a, 0x0d
|
||||
call conout
|
||||
ld a, 0x0a
|
||||
call conout
|
||||
ret
|
||||
|
||||
print_board:
|
||||
call print_newline
|
||||
call print_newline
|
||||
call print_box_break_horizontal
|
||||
call print_newline
|
||||
ld c, 0 ; index
|
||||
pb_loop:
|
||||
ld a, c
|
||||
call print_element
|
||||
inc c ; advance to next index
|
||||
; horizontal box break at the top of each band of three rows
|
||||
ld hl, table_move2box_y
|
||||
ld b, 0
|
||||
add hl, bc
|
||||
ld a, (hl)
|
||||
or a
|
||||
jr nz, pb_boxh
|
||||
ld hl, table_move2x
|
||||
ld b, 0
|
||||
add hl, bc
|
||||
ld a, (hl)
|
||||
or a
|
||||
jr nz, pb_boxh
|
||||
call print_newline
|
||||
call print_box_break_horizontal
|
||||
pb_boxh:
|
||||
ld hl, table_move2x
|
||||
ld b, 0
|
||||
add hl, bc
|
||||
ld a, (hl)
|
||||
or a
|
||||
jr nz, pb_newl
|
||||
call print_newline
|
||||
jr pb_cont
|
||||
pb_newl:
|
||||
ld hl, table_move2box_x
|
||||
ld b, 0
|
||||
add hl, bc
|
||||
ld a, (hl)
|
||||
or a
|
||||
jr nz, pb_cont
|
||||
call print_box_break_vertical
|
||||
pb_cont:
|
||||
ld a, c
|
||||
cp 81
|
||||
jp nz, pb_loop
|
||||
ret
|
||||
|
||||
; ----------------------------------------------------------------------------
|
||||
; Strings (nul-terminated)
|
||||
; ----------------------------------------------------------------------------
|
||||
msg_solving: db "Solving puzzle: ", 0
|
||||
msg_pass: db "pass", 0
|
||||
msg_fail: db "fail", 0
|
||||
msg_hbreak: db " --------+---------+--------", 0
|
||||
|
||||
; ----------------------------------------------------------------------------
|
||||
; The puzzle (clues). Lives in the TPA (RAM) so it is mutated in place.
|
||||
; "World's hardest sudoku".
|
||||
; ----------------------------------------------------------------------------
|
||||
puzzle:
|
||||
db 8,0,0,0,0,0,0,0,0
|
||||
db 0,0,3,6,0,0,0,0,0
|
||||
db 0,7,0,0,9,0,2,0,0
|
||||
db 0,5,0,0,0,7,0,0,0
|
||||
db 0,0,0,0,4,5,7,0,0
|
||||
db 0,0,0,1,0,0,0,3,0
|
||||
db 0,0,1,0,0,0,0,6,8
|
||||
db 0,0,8,5,0,0,0,1,0
|
||||
db 0,9,0,0,0,0,4,0,0
|
||||
|
||||
; ----------------------------------------------------------------------------
|
||||
; Lookup tables. The three used while printing get a trailing sentinel so the
|
||||
; index-81 read at the end of the board loop is defined (matches the 6502).
|
||||
; ----------------------------------------------------------------------------
|
||||
table_move2x:
|
||||
db 0,1,2,3,4,5,6,7,8
|
||||
db 0,1,2,3,4,5,6,7,8
|
||||
db 0,1,2,3,4,5,6,7,8
|
||||
db 0,1,2,3,4,5,6,7,8
|
||||
db 0,1,2,3,4,5,6,7,8
|
||||
db 0,1,2,3,4,5,6,7,8
|
||||
db 0,1,2,3,4,5,6,7,8
|
||||
db 0,1,2,3,4,5,6,7,8
|
||||
db 0,1,2,3,4,5,6,7,8
|
||||
db 0
|
||||
|
||||
table_move2box_x:
|
||||
db 0,1,2,0,1,2,0,1,2
|
||||
db 0,1,2,0,1,2,0,1,2
|
||||
db 0,1,2,0,1,2,0,1,2
|
||||
db 0,1,2,0,1,2,0,1,2
|
||||
db 0,1,2,0,1,2,0,1,2
|
||||
db 0,1,2,0,1,2,0,1,2
|
||||
db 0,1,2,0,1,2,0,1,2
|
||||
db 0,1,2,0,1,2,0,1,2
|
||||
db 0,1,2,0,1,2,0,1,2
|
||||
db 0
|
||||
|
||||
table_move2box_y:
|
||||
db 0,0,0,0,0,0,0,0,0
|
||||
db 1,1,1,1,1,1,1,1,1
|
||||
db 2,2,2,2,2,2,2,2,2
|
||||
db 0,0,0,0,0,0,0,0,0
|
||||
db 1,1,1,1,1,1,1,1,1
|
||||
db 2,2,2,2,2,2,2,2,2
|
||||
db 0,0,0,0,0,0,0,0,0
|
||||
db 1,1,1,1,1,1,1,1,1
|
||||
db 2,2,2,2,2,2,2,2,2
|
||||
db 0
|
||||
|
||||
table_move2row_start:
|
||||
db 0,0,0,0,0,0,0,0,0
|
||||
db 9,9,9,9,9,9,9,9,9
|
||||
db 18,18,18,18,18,18,18,18,18
|
||||
db 27,27,27,27,27,27,27,27,27
|
||||
db 36,36,36,36,36,36,36,36,36
|
||||
db 45,45,45,45,45,45,45,45,45
|
||||
db 54,54,54,54,54,54,54,54,54
|
||||
db 63,63,63,63,63,63,63,63,63
|
||||
db 72,72,72,72,72,72,72,72,72
|
||||
|
||||
table_move2box_start:
|
||||
db 0,0,0,3,3,3,6,6,6
|
||||
db 0,0,0,3,3,3,6,6,6
|
||||
db 0,0,0,3,3,3,6,6,6
|
||||
db 27,27,27,30,30,30,33,33,33
|
||||
db 27,27,27,30,30,30,33,33,33
|
||||
db 27,27,27,30,30,30,33,33,33
|
||||
db 54,54,54,57,57,57,60,60,60
|
||||
db 54,54,54,57,57,57,60,60,60
|
||||
db 54,54,54,57,57,57,60,60,60
|
||||
|
||||
prog_end: ; end of code + data (this is the saved image)
|
||||
|
||||
; ----------------------------------------------------------------------------
|
||||
; Stack: grows down from stacktop, which sits in TPA RAM just past the image
|
||||
; (not part of the saved binary). Plenty for ~60 levels of recursion.
|
||||
; ----------------------------------------------------------------------------
|
||||
ds 512
|
||||
stacktop:
|
||||
|
||||
SAVEBIN "sudoku.com", 0x0100, prog_end-0x0100
|
||||
SAVEHEX "sudoku.hex", 0x0100, prog_end-0x0100
|
||||
Binary file not shown.
@@ -0,0 +1,64 @@
|
||||
:1001000031E30621C202CD39010E00CD4601B720F0
|
||||
:100110000B21D302CD3901CD7202180621D802CDB0
|
||||
:1001200039013E0ACD2A01C30000F5C5D5E55F0EB1
|
||||
:1001300002CD0500E1D1C1F1C9F57EB72806CD2A6F
|
||||
:10014000012318F6F1C979FE512002AFC921FA0244
|
||||
:100150000600097EB728040CC34601060178CD8A43
|
||||
:100160000138127821FA021600591977C50CCD46CC
|
||||
:1001700001C1B728130478FE0AC25D0121FA0216F4
|
||||
:1001800000591936003E01C9AFC9C5214104060016
|
||||
:10019000095E21FA02160019BE282223BE281E235A
|
||||
:1001A000BE281A23BE281623BE281223BE280E23DB
|
||||
:1001B000BE280A23BE280623BE28021803C3310224
|
||||
:1001C000214B030600095E21FA02160019110900ED
|
||||
:1001D000BE282219BE281E19BE281A19BE281619B3
|
||||
:1001E000BE281219BE280E19BE280A19BE280619E3
|
||||
:1001F000BE28021803C331022192040600095E21C1
|
||||
:10020000FA02160019BE282923BE282523BE28215C
|
||||
:1002100011070019BE281A23BE281623BE28121162
|
||||
:10022000070019BE280B23BE280723BE2803C1B729
|
||||
:10023000C9C137C9E5C54F3E20CD2A0121FA0206C2
|
||||
:1002400000097EB72807C630CD2A0118053E2DCDFE
|
||||
:100250002A013E20CD2A01C1E1C93E7CCD2A01C937
|
||||
:1002600021DD02CD3901C93E0DCD2A013E0ACD2A3C
|
||||
:1002700001C9CD6702CD6702CD6002CD67020E00D5
|
||||
:1002800079CD34020C21EF030600097EB72010213E
|
||||
:100290004B030600097EB72006CD6702CD60022120
|
||||
:1002A0004B030600097EB72005CD6702180D219D7E
|
||||
:1002B000030600097EB72003CD5A0279FE51C280A1
|
||||
:1002C00002C9536F6C76696E672070757A7A6C65B7
|
||||
:1002D0003A200070617373006661696C00202D2DF7
|
||||
:1002E0002D2D2D2D2D2D2B2D2D2D2D2D2D2D2D2D40
|
||||
:1002F0002B2D2D2D2D2D2D2D2D0008000000000063
|
||||
:1003000000000000000306000000000000070000DD
|
||||
:1003100009000200000005000000070000000000C6
|
||||
:1003200000000405070000000000010000000300B9
|
||||
:1003300000000100000000060800000805000000A1
|
||||
:100340000100000900000000040000000102030495
|
||||
:10035000050607080001020304050607080001025C
|
||||
:100360000304050607080001020304050607080048
|
||||
:10037000010203040506070800010203040506073D
|
||||
:100380000800010203040506070800010203040532
|
||||
:100390000607080001020304050607080000010221
|
||||
:1003A000000102000102000102000102000102003E
|
||||
:1003B000010200010200010200010200010200012D
|
||||
:1003C000020001020001020001020001020001021C
|
||||
:1003D000000102000102000102000102000102000E
|
||||
:1003E00001020001020001020001020001020000FE
|
||||
:1003F00000000000000000000101010101010101F5
|
||||
:1004000001020202020202020202000000000000D9
|
||||
:1004100000000001010101010101010102020202CB
|
||||
:1004200002020202020000000000000000000101C0
|
||||
:1004300001010101010101020202020202020202A3
|
||||
:100440000000000000000000000009090909090976
|
||||
:100450000909091212121212121212121B1B1B1B73
|
||||
:100460001B1B1B1B1B2424242424242424242D2D67
|
||||
:100470002D2D2D2D2D2D2D3636363636363636365B
|
||||
:100480003F3F3F3F3F3F3F3F3F484848484848483D
|
||||
:1004900048480000000303030606060000000303AB
|
||||
:1004A000030606060000000303030606061B1B1BCB
|
||||
:1004B0001E1E1E2121211B1B1B1E1E1E2121211B56
|
||||
:1004C0001B1B1E1E1E2121213636363939393C3C74
|
||||
:1004D0003C3636363939393C3C3C36363639393992
|
||||
:0304E0003C3C3C65
|
||||
:00000001FF
|
||||
Reference in new issue
Block a user