The two prizes are not mutually inclusive — here’s why
Prize A — $620 (Baillie, Wagstaff, 1980): Find a composite that passes the original BPSW test: spsp(2) AND lpsp(P,Q) using Method A*. The vpsp condition is not required. This is the looser, 1980 formulation.
Prize B — $2000+ (Baillie, Fiori, Wagstaff, 2020): Find a composite passing the enhanced test: spsp(2) + slpsp + vpsp + Euler-Q. Or publish a peer-reviewed proof that none exist.
Prior works -
Baillie-PSW Prize - All Cores Fire (prize7.zip) ($620 Prize A)
seven prize.zip (168.0 KB)
; =============================================================================
; HDGL — BAILLIE-PSW $620 PRIZE HUNTER v3
; =============================================================================
;
; BUILD: nasm -f bin prize620_v3.asm -o prize620_v3.img
; QEMU: qemu-system-x86_64 -smp 4 -m 128M -drive format=raw,file=prize620_v3.img -boot c
;
; ARCHITECTURE — phi-lattice substrate is the engine, BPSW rides the gaps:
; Core 0 (FIRE): phi-lattice operator + BPSW on candidates ≡ 3 (mod 8)
; Core 1 (WATER): inverse verifier + BPSW on candidates ≡ 5 (mod 8)
; Core 2 (EARTH): N_phi oracle + BPSW on candidates ≡ 7 (mod 8)
; Core 3 (WIND): T(X) residual + BPSW on candidates ≡ 1 (mod 8)
;
; Each AP: after signalling DONE_K, immediately runs one BPSW step.
; Substrate integrity is preserved. BPSW fills the gaps between ticks.
; AP count auto-detected via CPUID. Fallback to single-core if APs fail.
;
; GATES — exact 1980 Baillie-PSW definition, NO vpsp:
; Gate 1: spsp(2) Miller-Rabin base 2
; Gate 2: slpsp Strong Lucas, Selfridge Method A adaptive D
; A pseudoprime here wins the $620. Row 14 lights red.
;
; RESUME — on warm reboot, continues from where it left off:
; Saves candidate positions to 0x9F000 every display cycle.
; Magic: 0x4844474C42505357 ("HDGLBPSW") + XOR checksum.
;
; PROGRESS — Row 13 shows human-readable percent and ETA:
; Coverage = total_tested / 2^63 (4 cores * half the odd range each)
; Displayed as X.XXXX% and estimated hours/days to 2^64.
;
; =============================================================================
BITS 16
ORG 0x7C00
; =============================================================================
; CONSTANTS
; =============================================================================
PAYLOAD_PHYS equ 0x00010000 ; unused, kept for reference only
; Payload (sectors 2..IMAGE_SECTORS) now loads at physical 0x7E00, directly
; after the boot sector, spanning up to roughly 0x7E00 + IMAGE_SECTORS*512.
; The AP trampoline and page tables MUST live outside that span or the
; code overwrites itself the moment build_page_tables or the AP-trampoline
; copy runs. 0x20000+ is comfortably clear.
AP_TRAMP_PHYS equ 0x00020000
PML4_PHYS equ 0x00021000
PDPT_PHYS equ 0x00022000
PD0_PHYS equ 0x00023000
PD1_PHYS equ 0x00024000
PD2_PHYS equ 0x00025000
PD3_PHYS equ 0x00026000
BSP_STACK equ 0x00070000
AP_STACK_BASE equ 0x00200000 ; above 1MB, safe from VGA/ROM
AP_STACK_STRIDE equ 0x00008000 ; 32KB per AP
LAPIC_BASE equ 0xFEE00000
LAPIC_ICR_LOW equ 0x300
LAPIC_ICR_HIGH equ 0x310
VGA_BASE equ 0x000B8000
VGA_COLS equ 80 ; characters per row
VGA_ROW equ 160 ; bytes per row
PRINT_EVERY equ 1048576
PRINT_MASK equ PRINT_EVERY - 1
IMAGE_SECTORS equ 64
PAYLOAD_SECTORS equ IMAGE_SECTORS - 1
; =============================================================================
; SHARED STATE LAYOUT (at 0x500000)
; =============================================================================
STATE_A equ 0x00500000 ; Current Omega: a
STATE_B equ 0x00500008 ; Current Omega: b
STATE_K equ 0x00500010 ; Iteration counter
FIRE_A equ 0x00500020 ; FIRE result: a+b
FIRE_B equ 0x00500028 ; FIRE result: a
FIRE_K equ 0x00500030 ; FIRE step counter
WATER_A equ 0x00500040 ; WATER result: b
WATER_B equ 0x00500048 ; WATER result: a-b
EARTH_N equ 0x00500060 ; N_phi(current)
EARTH_N_FIRE equ 0x00500068 ; N_phi(FIRE(current))
EARTH_DELTA equ 0x00500070 ; N_phi(FIRE) - N_phi(current)
EARTH_PREV_DELTA equ 0x00500078 ; Previous delta (for pattern check)
WIND_RES_A equ 0x00500080 ; T(X) phi-coefficient residual
WIND_RES_B equ 0x00500088 ; T(X) constant residual
WIND_FIX equ 0x00500090 ; 1 if at fixed point
REQUEST_K equ 0x005000A0 ; Published step for APs
DONE_WATER equ 0x005000A8
DONE_EARTH equ 0x005000B0
DONE_WIND equ 0x005000B8
READY_MASK equ 0x005000C0
ORACLE equ 0x005000C8 ; Wu-Wei oracle bitfield
TRINARY equ 0x005000D0 ; Trinary projection of N
STRATEGY equ 0x005000D8 ; Current strategy index
YIN equ 0x005000E0 ; Yin: s -> s^2 - 2
PHASE equ 0x005000E8 ; Completion phase 0->3->0
DEPTH equ 0x005000F0 ; Total iteration depth
CPU_COUNT equ 0x00500100
PARALLEL_MODE equ 0x00500108
ORACLE_AP_TIMEOUT_FLAG equ 0x00500110 ; 1 if AP bring-up timed out and we fell back to serial
; ─── Fibonacci–Legendre probable-prime oracle ───
; Verified theorem: for prime p != 5, p divides F_(p-(5|p)), where (5|p) is
; the Legendre symbol (whether 5 is a QR mod p). Tested against trial
; division for P=2..1999 in Python: zero false negatives (every real prime
; passes), a small known set of Fibonacci-pseudoprime false positives
; (25, 60, 323, 377, ...). This is a genuine probable-primality test, not
; a certified one -- displayed and labeled as such.
; ── Per-core BPSW state (stride 0x80, 4 cores) ──────────────────────────
BPSW_CORE_STRIDE equ 0x80
BPSW_C0 equ 0x00500120 ; Core 0 (FIRE) residue 3 mod 8
BPSW_C1 equ 0x005001A0 ; Core 1 (WATER) residue 5 mod 8
BPSW_C2 equ 0x00500220 ; Core 2 (EARTH) residue 7 mod 8
BPSW_C3 equ 0x005002A0 ; Core 3 (WIND) residue 1 mod 8
; Offsets within each core block (all 8-byte qwords)
BC_CAND equ 0x00 ; current candidate
BC_TESTED equ 0x08 ; candidates tested by this core
BC_SPSP2 equ 0x10 ; passed MR-2
BC_BPSW equ 0x18 ; passed both gates (probable primes)
BC_PSEUDO equ 0x20 ; composites passing both (THE PRIZE)
BC_LAST_PSEUDO equ 0x28 ; most recent pseudoprime
BC_SEL_D equ 0x30 ; last Selfridge D
BC_SEL_Q equ 0x38 ; last Selfridge Q
BC_MR2 equ 0x40 ; last MR-2 result
BC_SLC equ 0x48 ; last slpsp result
; ── Shared aggregate totals ────────────────────────────────────────────────
TOT_TESTED equ 0x00500320
TOT_SPSP2 equ 0x00500328
TOT_BPSW equ 0x00500330
TOT_PSEUDO equ 0x00500338
TOT_LAST_PSEUDO equ 0x00500340
TOT_RATE_RAW equ 0x00500348 ; (delta_tested<<20)/delta_tsc
TOT_TSC_PREV equ 0x00500350
TOT_TEST_PREV equ 0x00500358
; ── Resume record at 0x9F000 (survives warm reboot) ───────────────────────
RESUME_BASE equ 0x0009F000
RESUME_MAGIC equ 0x4844474C42505357 ; "HDGLBPSW"
RESUME_OFF_MAGIC equ 0x00
RESUME_OFF_C0 equ 0x08
RESUME_OFF_C1 equ 0x10
RESUME_OFF_C2 equ 0x18
RESUME_OFF_C3 equ 0x20
RESUME_OFF_CKSUM equ 0x28 ; XOR of all four candidates
RESUME_OFF_VER equ 0x30 ; version = 3
; ── Lucas scratch per core at 0x501000, stride 0x100 ──────────────────────
LS_BASE equ 0x00501000
LS_STRIDE equ 0x100
; Offsets within each core's scratch block
LSO_U equ 0x00 ; LSO_V equ 0x08 ; LSO_QK equ 0x10 ; LSO_U2 equ 0x18
LSO_V equ 0x08
LSO_QK equ 0x10
LSO_U2 equ 0x18
LSO_V2 equ 0x20
LSO_N equ 0x28
LSO_D equ 0x30
LSO_Q equ 0x38
LSO_INV2 equ 0x40
LSO_S equ 0x48
LSO_DODD equ 0x50
LSO_VD equ 0x60 ; V_d saved (for display)
; Legacy single-core compat (FIRE / core 0 maps here)
PRIME_CANDIDATE equ BPSW_C0 + BC_CAND
PRIME_FOUND_COUNT equ BPSW_C0 + BC_BPSW
PRIME_LAST_FOUND equ BPSW_C0 + BC_LAST_PSEUDO
; Must be a power of 2 (gated via bitmask test, not DIV). Higher = faster
; substrate tick rate, slower prime-scan rate. 64 recovers most of the
; ~47x throughput lost when testing every tick.
; Doubled from 64 to compensate: the full two-coefficient Frobenius test
; costs ~1.8-2x the modmuls of the old single-coefficient test (measured:
; 59 false positives -> 1, for that price).
PRIME_TEST_STRIDE equ 128
; Bounded spin count for waiting on AP ready bits. Large enough to give
; genuinely slow-but-working hardware a fair chance, small enough that a
; truly broken AP path fails over to serial mode in well under a second
; rather than hanging the boot forever.
AP_WAIT_TIMEOUT equ 5000000 ; large enough for QEMU + real hw
; Physical address adjustment for 64-bit code
; Label values are ORG-relative (0x7C00+), actual physical = label + PHYS_ADJ
; Payload now loads at physical 0x7E00 (immediately after the boot sector)
; in BOTH build variants -- HDD build loads it there itself, CD build gets
; it there for free via El Torito boot-load-size. Since ORG=0x7C00 and the
; boot sector is exactly 512 bytes, every label's value already equals its
; physical address: label(L) = 0x7C00 + file_offset(L) = physical(L).
; No adjustment needed. Kept as 0 so existing "+ PHYS_ADJ" references
; throughout the file remain valid no-ops.
PHYS_ADJ equ 0
; Strategy indices
STRATEGY_FLOWING equ 0 ; Healthy oscillation
STRATEGY_NONACTION equ 1 ; Hold on anomaly
STRATEGY_REDIRECT equ 2 ; Rebase on magnitude error
STRATEGY_CONVERGE equ 3 ; Fixed point detected
STRATEGY_CRITICAL equ 7 ; Halt
; Oracle bits
ORACLE_WATER_BROKEN equ 0x01
ORACLE_EARTH_PATTERN equ 0x02
ORACLE_EARTH_MAGNITUDE equ 0x04
ORACLE_WIND_FIXED equ 0x08
ORACLE_WIND_DIVERGE equ 0x10
ORACLE_CRITICAL equ 0x80
; =============================================================================
; BIOS BOOT
; =============================================================================
boot_start:
cli
xor ax, ax
mov ds, ax
mov es, ax
mov ss, ax
mov sp, 0x7C00
mov [boot_drive], dl
; Set video mode 3 (80x25 colour text) via BIOS INT 10h
; Forces NVS 295 or any GPU into a known text mode state
mov ax, 0x0003
int 0x10
; Print milestone 'B' via BIOS teletype (works before any VGA init)
mov ah, 0x0E
mov al, 'B'
xor bh, bh
int 0x10
%ifdef BUILD_CD
; ── CD / El Torito build ──
; boot-load-size in the boot catalog is set to load the ENTIRE image
; (all IMAGE_SECTORS sectors) directly to 0x7C00 before we ever run.
; Our own payload (sectors 2..N) is therefore ALREADY resident at
; physical 0x7E00 -- no disk read needed, and doing one would corrupt
; memory (CD LBAs are 2048-byte units, not 512-byte HDD units).
mov ah, 0x0E
mov al, 'C'
xor bh, bh
int 0x10
%else
; ── HDD / USB build ──
; BIOS legacy boot (INT 19h) loads only the 512-byte boot sector.
; We must load the payload ourselves, to physical 0x7E00 -- the SAME
; location El Torito uses for the CD build, so protected_entry lives
; at one fixed physical address regardless of boot path.
; Check INT13h extensions are present (AH=41h, BX=55AAh)
mov ah, 0x41
mov bx, 0x55AA
mov dl, [boot_drive]
int 0x13
jc .use_chs
cmp bx, 0xAA55
jne .use_chs
test cl, 1
jz .use_chs
; Extended read (AH=42h) into segment 0x07E0 (= physical 0x7E00)
mov si, disk_address_packet
mov dl, [boot_drive]
mov ah, 0x42
int 0x13
jnc .disk_ok
.use_chs:
; Legacy CHS fallback (AH=02h) for BIOSes without extensions.
; Read PAYLOAD_SECTORS sectors starting at C/H/S = 0/0/2 into 07E0:0000.
mov ax, 0x07E0
mov es, ax
xor bx, bx
mov ah, 0x02
mov al, PAYLOAD_SECTORS
mov ch, 0
mov cl, 2
mov dh, 0
mov dl, [boot_drive]
int 0x13
jc boot_disk_error
.disk_ok:
mov ah, 0x0E
mov al, 'H'
xor bh, bh
int 0x10
%endif
; Copy AP trampoline to 0x8000. Payload lives at physical 0x7E00 in
; BOTH build variants (loaded there by us for HDD, or by El Torito's
; boot-load-size for CD), same segment as the boot sector (DS=0),
; so no segment arithmetic needed either way.
mov ax, 0x2000 ; segment 0x2000 = physical 0x20000 = AP_TRAMP_PHYS
mov es, ax
mov si, ap_trampoline
xor di, di
mov cx, (ap_trampoline_end - ap_trampoline + 1) / 2
cld
rep movsw
xor ax, ax
mov es, ax
; Milestone 'T' — AP trampoline copy done
mov ah, 0x0E
mov al, 'T'
xor bh, bh
int 0x10
; A20 - Method 1: BIOS INT 15h AX=2401 (most portable)
mov ax, 0x2401
int 0x15
; A20 - Method 2: Port 0x92 Fast A20
in al, 0x92
or al, 00000010b
and al, 11111110b
out 0x92, al
; A20 - Method 3: Keyboard controller (KBC), bounded — cannot hang
call a20_kbc_enable
; Milestone 'A' — A20 sequence complete (all three methods attempted)
mov ah, 0x0E
mov al, 'A'
xor bh, bh
int 0x10
; GDT
lgdt [gdt_ptr]
; Milestone 'G' — GDT loaded
mov ah, 0x0E
mov al, 'G'
xor bh, bh
int 0x10
; Milestone 'P' — about to jump to protected mode (last real-mode print;
; if this is the last letter seen, the far jump or protected_entry itself
; is the failure point). MUST print before CR0.PE is set — BIOS
; interrupts don't work anymore once protected mode is enabled.
mov ah, 0x0E
mov al, 'P'
xor bh, bh
int 0x10
; Protected mode
mov eax, cr0
or eax, 1
mov cr0, eax
jmp dword 0x08:PROTECTED_ENTRY_PHYS
boot_disk_error:
mov si, boot_error_msg
.loop:
lodsb
test al, al
jz .halt
mov ah, 0x0E
xor bh, bh
int 0x10
jmp .loop
.halt:
cli
hlt
jmp .halt
; =============================================================================
; DISK ADDRESS PACKET
; =============================================================================
disk_address_packet:
db 0x10, 0x00
dw PAYLOAD_SECTORS
dw 0x0000
dw 0x07E0 ; segment 0x07E0 = physical 0x7E00, right after boot sector
dq 1
boot_drive: db 0
boot_error_msg: db "HDGL DISK ERROR",0
; A20 via keyboard controller — bounded retries, never hangs.
; Each wait loop gives up after KBC_TIMEOUT iterations rather than
; spinning forever on hardware with no PS/2 KBC or a non-conforming one.
KBC_TIMEOUT equ 65535
a20_kbc_enable:
call .kbc_wait_in
mov al, 0xAD ; disable keyboard
out 0x64, al
call .kbc_wait_in
mov al, 0xD0 ; read output port
out 0x64, al
call .kbc_wait_out
in al, 0x60
push ax
call .kbc_wait_in
mov al, 0xD1 ; write output port
out 0x64, al
call .kbc_wait_in
pop ax
or al, 2 ; set A20 bit
out 0x60, al
call .kbc_wait_in
mov al, 0xAE ; enable keyboard
out 0x64, al
call .kbc_wait_in
ret
.kbc_wait_in:
push cx
mov cx, KBC_TIMEOUT
.wi:
in al, 0x64
test al, 2
jz .wi_done
loop .wi
.wi_done:
pop cx
ret
.kbc_wait_out:
push cx
mov cx, KBC_TIMEOUT
.wo:
in al, 0x64
test al, 1
jnz .wo_done
loop .wo
.wo_done:
pop cx
ret
; =============================================================================
; GDT
; =============================================================================
align 8
gdt_base:
dq 0x0000000000000000 ; null
dq 0x00CF9A000000FFFF ; 0x08: 32-bit code
dq 0x00CF92000000FFFF ; 0x10: data (32 and 64 bit)
dq 0x00AF9A000000FFFF ; 0x18: 64-bit code
gdt_end:
gdt_ptr:
dw gdt_end - gdt_base - 1
dd gdt_base
; =============================================================================
; BOOT SECTOR PAD
; =============================================================================
times 510 - ($ - $$) db 0
dw 0xAA55
; =============================================================================
; PAYLOAD — 32-BIT PROTECTED MODE ENTRY
; =============================================================================
; File offset 512 = physical 0x10200 when loaded.
; PROTECTED_ENTRY_PHYS = 0x10000 + 512 = 0x10200
BITS 32
protected_entry:
cli
mov ax, 0x10
mov ds, ax
mov es, ax
mov ss, ax
mov esp, BSP_STACK
; Milestone '1' — reached 32-bit protected mode. Direct VGA write
; (no BIOS available here); bottom-left corner, out of the way.
mov byte [0xB8000 + 24*160 + 0], '1'
mov byte [0xB8000 + 24*160 + 1], 0x4F
; Build identity page tables (0..4 GiB, 2 MB pages)
call build_page_tables
; Milestone '2' — page tables built
mov byte [0xB8000 + 24*160 + 2], '2'
mov byte [0xB8000 + 24*160 + 3], 0x4F
; PAE
mov eax, cr4
or eax, (1 << 5)
mov cr4, eax
; EFER.LME
mov ecx, 0xC0000080
rdmsr
or eax, (1 << 8)
wrmsr
; CR3
mov eax, PML4_PHYS
mov cr3, eax
; Paging on
mov eax, cr0
or eax, (1 << 31)
mov cr0, eax
; Milestone '3' — paging enabled, about to enter long mode
mov byte [0xB8000 + 24*160 + 4], '3'
mov byte [0xB8000 + 24*160 + 5], 0x4F
; Far jump to 64-bit entry — LONG_MODE_ENTRY_PHYS computed below
jmp dword 0x18:LONG_MODE_ENTRY_PHYS
; =============================================================================
; PAGE TABLE CONSTRUCTION (32-bit)
; =============================================================================
build_page_tables:
pushad
; Zero PML4 + PDPT + 4 PDs = 6 pages = 0x6000 bytes
mov edi, PML4_PHYS
xor eax, eax
mov ecx, 0x6000 / 4
cld
rep stosd
; PML4[0] -> PDPT
mov dword [PML4_PHYS + 0], PDPT_PHYS | 0x003
mov dword [PML4_PHYS + 4], 0
; PDPT[0..3] -> PD0..PD3
mov dword [PDPT_PHYS + 0], PD0_PHYS | 0x003
mov dword [PDPT_PHYS + 4], 0
mov dword [PDPT_PHYS + 8], PD1_PHYS | 0x003
mov dword [PDPT_PHYS + 12], 0
mov dword [PDPT_PHYS + 16], PD2_PHYS | 0x003
mov dword [PDPT_PHYS + 20], 0
mov dword [PDPT_PHYS + 24], PD3_PHYS | 0x003
mov dword [PDPT_PHYS + 28], 0
; PD0: 0..1 GiB (512 entries × 2 MB = 1 GiB)
mov edi, PD0_PHYS
xor eax, eax
mov ecx, 512
.pd0:
mov edx, eax
or edx, 0x83 ; present + RW + huge (2MB)
mov [edi], edx
mov dword [edi+4], 0
add eax, 0x200000
add edi, 8
loop .pd0
; PD1: 1..2 GiB
mov edi, PD1_PHYS
mov eax, 0x40000000
mov ecx, 512
.pd1:
mov edx, eax
or edx, 0x83
mov [edi], edx
mov dword [edi+4], 0
add eax, 0x200000
add edi, 8
loop .pd1
; PD2: 2..3 GiB
mov edi, PD2_PHYS
mov eax, 0x80000000
mov ecx, 512
.pd2:
mov edx, eax
or edx, 0x83
mov [edi], edx
mov dword [edi+4], 0
add eax, 0x200000
add edi, 8
loop .pd2
; PD3: 3..4 GiB (wraps at 4 GiB, ok for identity map)
mov edi, PD3_PHYS
mov eax, 0xC0000000
mov ecx, 512
.pd3:
mov edx, eax
or edx, 0x83
mov [edi], edx
mov dword [edi+4], 0
add eax, 0x200000
add edi, 8
loop .pd3
popad
ret
; =============================================================================
; 64-BIT BSP ENTRY
; =============================================================================
; THIS LABEL MUST BE THE FIRST BITS 64 INSTRUCTION IN THE FILE.
; LONG_MODE_ENTRY_PHYS is computed from its file position.
BITS 64
long_mode_entry:
cli
mov ax, 0x10
mov ds, ax
mov es, ax
mov ss, ax
mov rsp, BSP_STACK
; Milestone '4' — reached 64-bit long mode (independent of COM1)
mov byte [0xB8000 + 24*160 + 6], '4'
mov byte [0xB8000 + 24*160 + 7], 0x4F
; COM1 serial init (115200 8N1)
; Works regardless of GPU - critical for bare metal debug
mov dx, 0x3F9
mov al, 0x00
out dx, al ; disable interrupts
mov dx, 0x3FB
mov al, 0x80
out dx, al ; DLAB=1
mov dx, 0x3F8
mov al, 0x01
out dx, al ; divisor lo = 1 (115200 baud)
mov dx, 0x3F9
mov al, 0x00
out dx, al ; divisor hi
mov dx, 0x3FB
mov al, 0x03
out dx, al ; 8N1, DLAB=0
mov dx, 0x3FC
mov al, 0x03
out dx, al ; RTS+DTR
; Send milestone 'L' = long mode entry confirmed
call serial_putchar_L ; 'L' = long mode
; Detect logical processor count via CPUID
mov eax, 1
cpuid
shr ebx, 16
and ebx, 0xFF
test ebx, ebx
jnz .cpu_ok
mov ebx, 1
.cpu_ok:
mov [CPU_COUNT], rbx
; (CPU count stored in CPU_COUNT)
; Enable multi-core if > 1 CPU detected. APs run substrate roles
; (WATER/EARTH/WIND) and fill gaps with BPSW on their residue class.
; Trampoline uses embedded GDT at fixed physical offset — no BIOS GDT.
; AP_WAIT_TIMEOUT fallback keeps single-core path safe on any hardware.
cmp rbx, 1
jle .force_serial
mov qword [PARALLEL_MODE], 1
jmp .mode_done
.force_serial:
.serial:
mov qword [PARALLEL_MODE], 0
.mode_done:
; ─── Canonical initial state: Ω = 0·φ + 1 ───
mov qword [STATE_A], 0
mov qword [STATE_B], 1
mov qword [STATE_K], 0
mov qword [FIRE_A], 0
mov qword [FIRE_B], 1
mov qword [FIRE_K], 0
mov qword [WATER_A], 0
mov qword [WATER_B], 1
mov qword [EARTH_N], 1 ; N(0,1) = 1
mov qword [EARTH_N_FIRE], 0
mov qword [EARTH_DELTA], 0
mov qword [EARTH_PREV_DELTA], 0 ; no previous delta yet
mov qword [WIND_RES_A], 0
mov qword [WIND_RES_B], 0
mov qword [WIND_FIX], 0
mov qword [REQUEST_K], 0
mov qword [DONE_WATER], 0
mov qword [DONE_EARTH], 0
mov qword [DONE_WIND], 0
mov qword [READY_MASK], 1
mov qword [ORACLE], 0
mov qword [TRINARY], 0
mov qword [STRATEGY], STRATEGY_FLOWING
mov qword [YIN], 2
mov qword [ORACLE_AP_TIMEOUT_FLAG], 0
; ── Zero all BPSW state and scratch ──
mov rdi, BPSW_C0
xor rax, rax
mov rcx, (0x200 + 4*BPSW_CORE_STRIDE) / 8
rep stosq
mov rdi, LS_BASE
mov rcx, (4 * LS_STRIDE) / 8
rep stosq
mov rdi, TOT_TESTED
mov rcx, 64 / 8
rep stosq
; ── Resume: check for valid save at 0x9F000 ──
mov rax, [RESUME_BASE + RESUME_OFF_MAGIC]
mov rbx, RESUME_MAGIC
cmp rax, rbx
jne .fresh_start
; Verify checksum: XOR of four candidates
mov rax, [RESUME_BASE + RESUME_OFF_C0]
xor rax, [RESUME_BASE + RESUME_OFF_C1]
xor rax, [RESUME_BASE + RESUME_OFF_C2]
xor rax, [RESUME_BASE + RESUME_OFF_C3]
cmp rax, [RESUME_BASE + RESUME_OFF_CKSUM]
jne .fresh_start
; Valid save — restore candidates
mov rax, [RESUME_BASE + RESUME_OFF_C0]
mov [BPSW_C0 + BC_CAND], rax
mov rax, [RESUME_BASE + RESUME_OFF_C1]
mov [BPSW_C1 + BC_CAND], rax
mov rax, [RESUME_BASE + RESUME_OFF_C2]
mov [BPSW_C2 + BC_CAND], rax
mov rax, [RESUME_BASE + RESUME_OFF_C3]
mov [BPSW_C3 + BC_CAND], rax
jmp .bpsw_init_done
.fresh_start:
; Core 0: residue 3 mod 8
mov qword [BPSW_C0 + BC_CAND], 3
; Core 1: residue 5 mod 8
mov qword [BPSW_C1 + BC_CAND], 5
; Core 2: residue 7 mod 8
mov qword [BPSW_C2 + BC_CAND], 7
; Core 3: residue 1 mod 8 (starts at 9; 1 is not prime)
mov qword [BPSW_C3 + BC_CAND], 9
; Clear resume record
mov qword [RESUME_BASE + RESUME_OFF_MAGIC], 0
.bpsw_init_done:
; Seed rate tracking with current TSC
rdtsc
shl rdx, 32
or rax, rdx
mov [TOT_TSC_PREV], rax
mov qword [TOT_TEST_PREV], 0
mov qword [PHASE], 0
mov qword [DEPTH], 0
; VGA init
call vga_init
; Launch APs if multi-core
cmp qword [PARALLEL_MODE], 1
jne .bsp_fire
call start_aps
; (parallel mode set by start_aps)
.bsp_fire:
call role_fire
.halt:
cli
hlt
jmp .halt
; =============================================================================
; START APPLICATION PROCESSORS
; =============================================================================
start_aps:
; Enable BSP LAPIC (xAPIC, bit 11 only)
mov ecx, 0x1B
rdmsr
mov r9, rax ; save original MSR value
or eax, (1 << 11)
and eax, ~(1 << 10) ; xAPIC only
wrmsr
; Get LAPIC MMIO base from MSR (bits 31:12)
mov eax, r9d
and eax, 0xFFFFF000
test eax, eax
jnz .got_lapic_base
mov eax, 0xFEE00000
.got_lapic_base:
mov r8d, eax
; ── INIT IPI sequence per Intel MP spec ──
; ICR_HIGH: destination = 0 (all-exc-self shorthand overrides this)
mov dword [r8 + 0x310], 0x00000000
; ICR_LOW: INIT, level assert, all-excluding-self shorthand
mov dword [r8 + 0x300], 0x000C4500
; Poll send-pending bit (bit 12)
.poll_init:
mov eax, [r8 + 0x300]
test eax, (1 << 12)
jnz .poll_init
; INIT de-assert
mov dword [r8 + 0x310], 0x00000000
mov dword [r8 + 0x300], 0x000C8500
.poll_deassert:
mov eax, [r8 + 0x300]
test eax, (1 << 12)
jnz .poll_deassert
; 10ms delay
push rcx
mov ecx, 300000
.init_delay: dec ecx
jnz .init_delay
pop rcx
; SIPI #1
mov dword [r8 + 0x310], 0x00000000
mov dword [r8 + 0x300], 0x000C4620
.poll_sipi1:
mov eax, [r8 + 0x300]
test eax, (1 << 12)
jnz .poll_sipi1
; 200µs delay
push rcx
mov ecx, 5000
.sipi1_delay: dec ecx
jnz .sipi1_delay
pop rcx
; SIPI #2
mov dword [r8 + 0x310], 0x00000000
mov dword [r8 + 0x300], 0x000C4620
.poll_sipi2:
mov eax, [r8 + 0x300]
test eax, (1 << 12)
jnz .poll_sipi2
; ── Wait for APs (50M iterations) ──
; Real 3GHz: ~67ms — more than enough
; QEMU (no KVM): may time out — falls back to single-core gracefully
mov r9, 50000000
.wait_aps:
mov rax, [READY_MASK]
and eax, 0xF
cmp eax, 0xF
je .aps_ok
dec r9
jnz .wait_aps
; Timed out — use however many APs responded
mov rax, [READY_MASK]
and eax, 0xF
cmp eax, 1 ; only BSP?
je .ap_fallback
jmp .aps_ok ; partial parallel is fine
.ap_fallback:
mov qword [PARALLEL_MODE], 0
mov qword [ORACLE_AP_TIMEOUT_FLAG], 1
ret
.aps_ok:
ret
; =============================================================================
; FIRE — CPU 0 (Operator / Strategy Selector)
; =============================================================================
role_fire:
mov qword [READY_MASK], 1 ; mark CPU 0 ready
fire_cycle:
; ── Snapshot current Ω ──
mov r8, [STATE_A]
mov r9, [STATE_B]
mov r10, [STATE_K]
; ── FIRE: (a,b) -> (a+b, a) ──
mov rax, r8
add rax, r9
mov [FIRE_A], rax
mov [FIRE_B], r8
inc r10
mov [FIRE_K], r10
; ── Serial or parallel path ──
cmp qword [PARALLEL_MODE], 0
je .serial
; Parallel: publish request and wait
mov [REQUEST_K], r10
.wait_water:
mov rax, [DONE_WATER]
cmp rax, r10
jne .wait_water
.wait_earth:
mov rax, [DONE_EARTH]
cmp rax, r10
jne .wait_earth
.wait_wind:
mov rax, [DONE_WIND]
cmp rax, r10
jne .wait_wind
jmp .commit
.serial:
call water_compute
call earth_compute
call wind_compute
.commit:
; ── Wu-Wei strategy selection ──
mov rax, [ORACLE]
call fire_select_strategy
; ── BPSW oracle — throttled on PRIME_TEST_STRIDE ──
mov rax, r10
test rax, (PRIME_TEST_STRIDE - 1)
jnz .skip_bpsw
; C0 always (FIRE owns this core's candidates)
mov r12, BPSW_C0
mov r13, LS_BASE
call bpsw_step
; C1/C2/C3: only in serial mode (parallel mode = APs own these)
cmp qword [PARALLEL_MODE], 0
jne .skip_bpsw
mov r12, BPSW_C1
mov r13, LS_BASE + LS_STRIDE
call bpsw_step
mov r12, BPSW_C2
mov r13, LS_BASE + LS_STRIDE*2
call bpsw_step
mov r12, BPSW_C3
mov r13, LS_BASE + LS_STRIDE*3
call bpsw_step
.skip_bpsw:
; ── Commit FIRE result as new canonical state ──
; (strategy may modify this later - for now, always advance)
mov rax, [FIRE_A]
mov rbx, [FIRE_B]
mov [STATE_A], rax
mov [STATE_B], rbx
mov [STATE_K], r10
; ── YIN: s -> s² - 2 ──
mov rax, [YIN]
imul rax, rax
sub rax, 2
mov [YIN], rax
; ── Completion: 0->1->2->3->0 ──
inc qword [PHASE]
and qword [PHASE], 3
; ── Depth ──
inc qword [DEPTH]
; ── Display every PRINT_EVERY iterations ──
mov rax, r10
test rax, PRINT_MASK
jnz fire_cycle
call vga_update
jmp fire_cycle
; ============================================================================
; FIRE: WU-WEI STRATEGY SELECTOR
; Input: rax = ORACLE bitfield
; ============================================================================
fire_select_strategy:
; CRITICAL: halt and display
test al, ORACLE_CRITICAL
jnz .critical
; EARTH magnitude wrong: redirect (rebase)
test al, ORACLE_EARTH_MAGNITUDE
jnz .redirect
; EARTH pattern broken: non-action
test al, ORACLE_EARTH_PATTERN
jnz .nonaction
; WIND convergence: log it
test al, ORACLE_WIND_FIXED
jnz .converge
; WATER broken: flag but continue
test al, ORACLE_WATER_BROKEN
jnz .water_anom
; All clear
mov qword [STRATEGY], STRATEGY_FLOWING
ret
.critical:
mov qword [STRATEGY], STRATEGY_CRITICAL
call vga_update ; force display
cli
hlt ; deliberate halt on critical
jmp .critical
.redirect:
mov qword [STRATEGY], STRATEGY_REDIRECT
; Rebase: reset STATE to (0,1) to restart from known phi seed
; In a more sophisticated version this would be a soft reset
ret
.nonaction:
mov qword [STRATEGY], STRATEGY_NONACTION
ret
.converge:
mov qword [STRATEGY], STRATEGY_CONVERGE
ret
.water_anom:
; Water anomaly with no other flags: continue but log
mov qword [STRATEGY], STRATEGY_FLOWING
ret
; =============================================================================
; WATER — CPU 1 (Inverse Verification)
; =============================================================================
; WATER(a,b) = (b, a-b)
; Checks: WATER(FIRE(Ω)) == Ω
; WATER(a+b, a) = (a, (a+b)-a) = (a, b) = Ω -- always true for exact arithmetic
; So ORACLE_WATER_BROKEN fires only on arithmetic error (impossible mod 2^64)
water_compute:
mov r8, [STATE_A]
mov r9, [STATE_B]
; Compute WATER of current state
mov rax, r9
mov rbx, r8
sub rbx, r9
mov [WATER_A], rax
mov [WATER_B], rbx
; Verify WATER(FIRE(Ω)) == Ω
; FIRE = (FIRE_A, FIRE_B) = (a+b, a)
; WATER(a+b, a) = (a, b) so check WATER_FIRE_A==STATE_A, WATER_FIRE_B==STATE_B
mov rcx, [FIRE_A]
mov rdx, [FIRE_B]
; WATER of FIRE: first = FIRE_B = a, second = FIRE_A - FIRE_B = b
cmp rdx, r8 ; FIRE_B == STATE_A?
jne .broken
mov rsi, rcx
sub rsi, rdx
cmp rsi, r9 ; FIRE_A - FIRE_B == STATE_B?
jne .broken
; Clear water bit in oracle
mov rax, [ORACLE]
and rax, ~ORACLE_WATER_BROKEN
mov [ORACLE], rax
ret
.broken:
or qword [ORACLE], ORACLE_WATER_BROKEN
or qword [ORACLE], ORACLE_CRITICAL ; water failure is always critical
ret
; =============================================================================
; WATER WORKER — CPU 1 (AP loop)
; =============================================================================
role_water:
xor r15d, r15d
lock or qword [READY_MASK], 2
mov r12, BPSW_C1
mov r13, LS_BASE + LS_STRIDE
.wait:
mov rax, [REQUEST_K]
cmp rax, r15
je .bpsw_water ; no new substrate tick — run BPSW
mov r15, rax
call water_compute
mov [DONE_WATER], r15
.bpsw_water:
call bpsw_step ; fills idle time between substrate ticks
jmp .wait
; =============================================================================
; EARTH — CPU 2 (N_phi Pattern Oracle)
; =============================================================================
; N_phi(a,b) = -a² + ab + b²
;
; WU-WEI: For Fibonacci pairs, N oscillates: N(k) = (-1)^k.
; Expected delta each step: -(EARTH_N)*2 (flips sign, magnitude 2)
; If delta != -2*N(prev): pattern broken -> ORACLE_EARTH_PATTERN
; If |delta| != 2: magnitude wrong -> ORACLE_EARTH_MAGNITUDE
earth_compute:
push r12
push r13
mov r8, [STATE_A]
mov r9, [STATE_B]
; ── N(current) = -a² + ab + b² ──
mov rax, r8
imul rax, r8
neg rax ; -a²
mov rbx, r8
imul rbx, r9
add rax, rbx ; -a² + ab
mov rbx, r9
imul rbx, r9
add rax, rbx ; -a² + ab + b²
mov [EARTH_N], rax
; ── N(FIRE(current)): FIRE=(a+b, a) ──
mov r10, r8
add r10, r9 ; r10 = a+b = FIRE_A
mov r11, r8 ; r11 = a = FIRE_B
mov rax, r10
imul rax, r10
neg rax
mov rbx, r10
imul rbx, r11
add rax, rbx
mov rbx, r11
imul rbx, r11
add rax, rbx
mov [EARTH_N_FIRE], rax
; ── Delta = N(FIRE) - N(current) ──
mov rcx, [EARTH_N]
mov rdx, [EARTH_N_FIRE]
mov rax, rdx
sub rax, rcx ; delta = N_fire - N_curr
mov [EARTH_DELTA], rax
; ── Pattern check: |delta| should be 2 ──
mov rbx, rax
; abs(rax): if negative, negate
test rax, rax
jns .pos
neg rbx
.pos:
cmp rbx, 2
jne .magnitude_wrong
; ── Sign check: delta should be opposite sign of N(current) ──
; N positive -> delta should be negative
; N negative -> delta should be positive
; i.e. N(current) * delta < 0 (opposite signs)
; Skip sign check on very first iteration (prev_delta == 0)
cmp qword [EARTH_PREV_DELTA], 0
je .first_iter
; Check alternation: delta sign should be opposite of prev_delta sign
mov r12, rax ; current delta
mov r13, [EARTH_PREV_DELTA]
; If both same sign -> pattern broken
; r12 and r13: test sign agreement via XOR of sign bits
mov r14, r12
xor r14, r13
; If bit 63 of XOR is 0, both same sign -> broken
test r14, r14
js .signs_ok
; Same sign = pattern broken
or qword [ORACLE], ORACLE_EARTH_PATTERN
jmp .done
.signs_ok:
; Pattern good: clear earth bits
mov rbx, [ORACLE]
and rbx, ~(ORACLE_EARTH_PATTERN | ORACLE_EARTH_MAGNITUDE)
mov [ORACLE], rbx
jmp .done
.first_iter:
; First iteration: just clear earth error bits
mov rbx, [ORACLE]
and rbx, ~(ORACLE_EARTH_PATTERN | ORACLE_EARTH_MAGNITUDE)
mov [ORACLE], rbx
jmp .done
.magnitude_wrong:
or qword [ORACLE], ORACLE_EARTH_MAGNITUDE
jmp .done
.done:
; Save delta for next iteration
mov rax, [EARTH_DELTA]
mov [EARTH_PREV_DELTA], rax
; ── Trinary projection: sign of N ──
mov rax, [EARTH_N]
test rax, rax
jz .tri_zero
js .tri_neg
mov qword [TRINARY], 1
pop r13
pop r12
ret
.tri_neg:
mov qword [TRINARY], -1
pop r13
pop r12
ret
.tri_zero:
mov qword [TRINARY], 0
pop r13
pop r12
ret
; =============================================================================
; EARTH WORKER — CPU 2 (AP loop)
; =============================================================================
role_earth:
push rax
mov al, 'E'
call serial_putchar
pop rax
xor r15d, r15d
lock or qword [READY_MASK], 4
mov r12, BPSW_C2
mov r13, LS_BASE + LS_STRIDE*2
.wait:
mov rax, [REQUEST_K]
cmp rax, r15
je .bpsw_earth
mov r15, rax
call earth_compute
mov [DONE_EARTH], r15
.bpsw_earth:
call bpsw_step
jmp .wait
; =============================================================================
; WIND — CPU 3 (T(X) Fixed-Point Residual)
; =============================================================================
; T(X) = 1 + 1/X. Fixed point: X = phi.
; In Z[phi] with X = a*phi + b:
; T(X) - X residuals:
; phi coeff: a² + 2ab - a
; const coeff: a² + b² - b - 1
; Both zero iff X = phi (the fixed point).
;
; WU-WEI: residuals grow as Fibonacci grows.
; WIND_FIXED fires when both are zero (rare, meaningful event).
; WIND_DIVERGE fires when |res_a| + |res_b| exceeds threshold.
WIND_DIV_THRESH equ 0x1000000000 ; ~68 billion: divergence threshold
wind_compute:
push r12
mov r8, [STATE_A]
mov r9, [STATE_B]
; ── phi-coeff residual: a² + 2ab - a ──
mov rax, r8
imul rax, r8 ; a²
mov rbx, r8
imul rbx, r9 ; ab
add rbx, rbx ; 2ab
add rax, rbx ; a² + 2ab
sub rax, r8 ; a² + 2ab - a
mov [WIND_RES_A], rax
; ── const residual: a² + b² - b - 1 ──
mov rcx, r8
imul rcx, r8 ; a²
mov rdx, r9
imul rdx, r9 ; b²
add rcx, rdx ; a² + b²
sub rcx, r9 ; a² + b² - b
dec rcx ; a² + b² - b - 1
mov [WIND_RES_B], rcx
; ── Fixed point check ──
test rax, rax
jnz .not_fixed
test rcx, rcx
jnz .not_fixed
mov qword [WIND_FIX], 1
or qword [ORACLE], ORACLE_WIND_FIXED
pop r12
ret
.not_fixed:
mov qword [WIND_FIX], 0
; ── Divergence check ──
; |res_a| + |res_b| > threshold?
mov rax, [WIND_RES_A]
test rax, rax
jns .pos_a
neg rax
.pos_a:
mov rbx, [WIND_RES_B]
test rbx, rbx
jns .pos_b
neg rbx
.pos_b:
add rax, rbx
mov r12, WIND_DIV_THRESH
cmp rax, r12
jbe .no_diverge
or qword [ORACLE], ORACLE_WIND_DIVERGE
jmp .wind_done
.no_diverge:
; Clear wind bits
mov rax, [ORACLE]
and rax, ~(ORACLE_WIND_FIXED | ORACLE_WIND_DIVERGE)
mov [ORACLE], rax
.wind_done:
pop r12
ret
; =============================================================================
; WIND WORKER — CPU 3 (AP loop)
; =============================================================================
role_wind:
push rax
mov al, 'N'
call serial_putchar
pop rax
xor r15d, r15d
lock or qword [READY_MASK], 8
mov r12, BPSW_C3
mov r13, LS_BASE + LS_STRIDE*3
.wait:
mov rax, [REQUEST_K]
cmp rax, r15
je .bpsw_wind
mov r15, rax
call wind_compute
mov [DONE_WIND], r15
.bpsw_wind:
call bpsw_step
jmp .wait
; =============================================================================
; FIBONACCI–LEGENDRE PROBABLE-PRIME ORACLE
; =============================================================================
;
; (modmul64 defined below with BPSW functions)
; =============================================================================
; modmul64: (RAX * RBX) mod RCX -> RAX
; =============================================================================
modmul64:
push rdx
mul rbx
div rcx
mov rax, rdx
pop rdx
ret
; =============================================================================
; pow_mod_int: base^exp mod m -> RAX
; RDI=base RSI=exp RDX=modulus
; =============================================================================
pow_mod_int:
push rbx
push rcx
push r8
push r9
push r10
mov r8, rdi
mov r9, rsi
mov r10, rdx
mov rax, 1
.pmi_loop:
test r9, r9
jz .pmi_done
test r9, 1
jz .pmi_sq
mov rbx, r8
mov rcx, r10
call modmul64
.pmi_sq:
push rax
mov rax, r8
mov rbx, r8
mov rcx, r10
call modmul64
mov r8, rax
pop rax
shr r9, 1
jmp .pmi_loop
.pmi_done:
pop r10
pop r9
pop r8
pop rcx
pop rbx
ret
; =============================================================================
; miller_rabin: strong pseudoprime test
; RDI=n RBX=base -> RAX=1(pass) 0(fail)
; =============================================================================
miller_rabin:
push rbx
push rcx
push rdx
push r8
push r9
push r10
push r11
mov r10, rdi
mov r11, rbx
mov r8, r10
dec r8
xor r9, r9
mov rcx, r8
.mr_find_sd:
test rcx, 1
jnz .mr_sd_done
shr rcx, 1
inc r9
jmp .mr_find_sd
.mr_sd_done:
mov rdi, r11
mov rsi, rcx
mov rdx, r10
call pow_mod_int
cmp rax, 1
je .mr_pass
cmp rax, r8
je .mr_pass
mov rdx, r9
dec rdx
jz .mr_fail
.mr_loop:
push rdx
push r8
push r10
mov rbx, rax
mov rcx, r10
call modmul64
pop r10
pop r8
pop rdx
cmp rax, r8
je .mr_pass
test rax, rax
jz .mr_fail
dec rdx
jnz .mr_loop
.mr_fail:
xor rax, rax
jmp .mr_ret
.mr_pass:
mov rax, 1
.mr_ret:
pop r11
pop r10
pop r9
pop r8
pop rdx
pop rcx
pop rbx
ret
; =============================================================================
; jacobi: (a|n) -> RAX in {-1,0,+1}
; RDI=a (signed) RSI=n (odd positive)
; =============================================================================
jacobi:
push rbx
push rcx
push rdx
push r8
push r9
push r10
push r11
mov r8, rdi
mov r9, rsi
mov r10, 1
mov rax, r8
cqo
mov rbx, r9
idiv rbx
mov r8, rdx
test r8, r8
jns .jac_a_ok
add r8, r9
.jac_a_ok:
.jac_loop:
test r8, r8
jz .jac_zero
cmp r8, 1
je .jac_ret
xor r11, r11
.jac_strip:
test r8, 1
jnz .jac_stripped
shr r8, 1
inc r11
jmp .jac_strip
.jac_stripped:
test r11, 1
jz .jac_eeven
mov rax, r9
and rax, 7
cmp rax, 3
je .jac_flip2
cmp rax, 5
je .jac_flip2
jmp .jac_eeven
.jac_flip2:
neg r10
.jac_eeven:
cmp r8, 1
je .jac_ret
mov rax, r8
and rax, 3
cmp rax, 3
jne .jac_no_qr
mov rax, r9
and rax, 3
cmp rax, 3
jne .jac_no_qr
neg r10
.jac_no_qr:
mov rax, r9
xor rdx, rdx
div r8
mov r9, r8
mov r8, rdx
jmp .jac_loop
.jac_zero:
xor rax, rax
jmp .jac_done
.jac_ret:
mov rax, r10
.jac_done:
pop r11
pop r10
pop r9
pop r8
pop rdx
pop rcx
pop rbx
ret
; =============================================================================
; selfridge_r: Selfridge Method A, stores D/Q into [R12+BC_SEL_D/Q]
; RDI=n R12=core block -> RAX=0 ok, 1 composite
; =============================================================================
selfridge_r:
push rbx
push rcx
push rdx
push r8
push r9
mov r8, rdi
mov r9, 5
xor rcx, rcx
.sel_loop:
mov rax, r9
test rcx, rcx
jz .sel_dpos
neg rax
.sel_dpos:
mov [r12 + BC_SEL_D], rax
mov rdi, rax
mov rsi, r8
call jacobi
cmp rax, -1
je .sel_found
cmp rax, 0
je .sel_jzero
add r9, 2
xor rcx, 1
cmp r9, 10000
jb .sel_loop
mov rax, 1
jmp .sel_done
.sel_jzero:
cmp r8, r9
je .sel_found
mov rax, 1
jmp .sel_done
.sel_found:
mov rdx, [r12 + BC_SEL_D]
mov rax, 1
sub rax, rdx
sar rax, 2
mov [r12 + BC_SEL_Q], rax
xor rax, rax
.sel_done:
pop r9
pop r8
pop rdx
pop rcx
pop rbx
ret
; =============================================================================
; strong_lucas_r: strong Lucas probable prime (NO vpsp)
; RDI=n R12=core block (reads BC_SEL_D/Q) R13=Lucas scratch base
; -> RAX=1 pass (slpsp), 0 fail
; =============================================================================
strong_lucas_r:
push rbx
push rcx
push rdx
push r8
push r9
push r10
push r11
push r14
push r15
mov r15, rdi
mov [r13 + LSO_N], r15
mov rax, [r12 + BC_SEL_D]
cqo
mov rbx, r15
idiv rbx
mov rax, rdx
test rax, rax
jns .sl_dok
add rax, r15
.sl_dok:
mov [r13 + LSO_D], rax
mov rax, [r12 + BC_SEL_Q]
cqo
mov rbx, r15
idiv rbx
mov rax, rdx
test rax, rax
jns .sl_qok
add rax, r15
.sl_qok:
mov [r13 + LSO_Q], rax
mov rax, r15
inc rax
shr rax, 1
mov [r13 + LSO_INV2], rax
mov rax, r15
inc rax
xor r8, r8
.sl_strip:
test rax, 1
jnz .sl_stripped
shr rax, 1
inc r8
jmp .sl_strip
.sl_stripped:
mov [r13 + LSO_S], r8
mov [r13 + LSO_DODD], rax
xor rax, rax
mov [r13 + LSO_U], rax
mov rax, 2
mov [r13 + LSO_V], rax
mov rax, 1
mov [r13 + LSO_QK], rax
mov rax, [r13 + LSO_DODD]
bsr r9, rax
.sl_bit:
; Double
mov rax, [r13 + LSO_U]
mov rbx, [r13 + LSO_V]
mov rcx, [r13 + LSO_N]
call modmul64
mov [r13 + LSO_U2], rax
mov rax, [r13 + LSO_V]
mov rbx, [r13 + LSO_V]
mov rcx, [r13 + LSO_N]
call modmul64
mov rbx, [r13 + LSO_QK]
add rbx, rbx
cmp rbx, [r13 + LSO_N]
jb .sl_qk2ok
sub rbx, [r13 + LSO_N]
.sl_qk2ok:
sub rax, rbx
jns .sl_v2ok
add rax, [r13 + LSO_N]
.sl_v2ok:
mov [r13 + LSO_V2], rax
mov rax, [r13 + LSO_QK]
mov rbx, [r13 + LSO_QK]
mov rcx, [r13 + LSO_N]
call modmul64
mov rbx, [r13 + LSO_U2]
mov [r13 + LSO_U], rbx
mov rbx, [r13 + LSO_V2]
mov [r13 + LSO_V], rbx
mov [r13 + LSO_QK], rax
; Add if bit set
mov rax, [r13 + LSO_DODD]
mov rcx, r9
shr rax, cl
test rax, 1
jz .sl_noadd
mov rax, [r13 + LSO_U]
mov [r13 + LSO_U2], rax
add rax, [r13 + LSO_V]
cmp rax, [r13 + LSO_N]
jb .sl_uok
sub rax, [r13 + LSO_N]
.sl_uok:
mov rbx, [r13 + LSO_INV2]
mov rcx, [r13 + LSO_N]
call modmul64
mov [r13 + LSO_U], rax
mov rax, [r13 + LSO_D]
mov rbx, [r13 + LSO_U2]
mov rcx, [r13 + LSO_N]
call modmul64
add rax, [r13 + LSO_V]
cmp rax, [r13 + LSO_N]
jb .sl_vok
sub rax, [r13 + LSO_N]
.sl_vok:
mov rbx, [r13 + LSO_INV2]
mov rcx, [r13 + LSO_N]
call modmul64
mov [r13 + LSO_V], rax
mov rax, [r13 + LSO_QK]
mov rbx, [r13 + LSO_Q]
mov rcx, [r13 + LSO_N]
call modmul64
mov [r13 + LSO_QK], rax
.sl_noadd:
test r9, r9
jz .sl_dd
dec r9
jmp .sl_bit
.sl_dd:
; Save V_d for display
mov rax, [r13 + LSO_V]
mov [r13 + LSO_VD], rax
; Strong pass: U_d=0 or V_d=0 (r=0)
mov rax, [r13 + LSO_U]
test rax, rax
jz .sl_pass
mov rax, [r13 + LSO_V]
test rax, rax
jz .sl_pass
; V_{d*2^r}=0 for r=1..s-1
mov r8, [r13 + LSO_S]
test r8, r8
jz .sl_fail
.sl_vloop:
dec r8
jz .sl_fail
mov rax, [r13 + LSO_V]
mov rbx, [r13 + LSO_V]
mov rcx, [r13 + LSO_N]
call modmul64
mov rbx, [r13 + LSO_QK]
add rbx, rbx
cmp rbx, [r13 + LSO_N]
jb .sl_v2a
sub rbx, [r13 + LSO_N]
.sl_v2a:
sub rax, rbx
jns .sl_v2b
add rax, [r13 + LSO_N]
.sl_v2b:
mov [r13 + LSO_V], rax
test rax, rax
jz .sl_pass
mov rax, [r13 + LSO_QK]
mov rbx, [r13 + LSO_QK]
mov rcx, [r13 + LSO_N]
call modmul64
mov [r13 + LSO_QK], rax
jmp .sl_vloop
.sl_fail:
xor rax, rax
jmp .sl_ret
.sl_pass:
mov rax, 1
.sl_ret:
pop r15
pop r14
pop r11
pop r10
pop r9
pop r8
pop rdx
pop rcx
pop rbx
ret
; =============================================================================
; bpsw_step: one candidate for the core in R12/R13
; R12 = core block base R13 = Lucas scratch base
; Advances BC_CAND by 8 (core owns 1 of 4 odd residues mod 8)
; =============================================================================
bpsw_step:
push rax
push rbx
push rcx
push rdx
push rdi
push rsi
push r8
push r9
push r10
push r11
push r12
push r13
push r14
push r15
mov rdi, [r12 + BC_CAND]
cmp rdi, 3
jb .bs_advance
; Quick trial division — flag confirmed composites
call trial_div_r14 ; R14 = 1 if composite
inc qword [r12 + BC_TESTED]
lock inc qword [TOT_TESTED]
; Gate 1: MR-2
mov rbx, 2
call miller_rabin
mov [r12 + BC_MR2], rax
test rax, rax
jz .bs_advance
lock inc qword [TOT_SPSP2]
inc qword [r12 + BC_SPSP2]
; Selfridge D/Q
mov rdi, [r12 + BC_CAND]
call selfridge_r
test rax, rax
jnz .bs_advance
; Gate 2: strong Lucas
mov rdi, [r12 + BC_CAND]
call strong_lucas_r
mov [r12 + BC_SLC], rax
test rax, rax
jz .bs_advance
; Both gates pass
inc qword [r12 + BC_BPSW]
lock inc qword [TOT_BPSW]
; Confirmed composite = PRIZE
test r14, r14
jz .bs_advance
mov rax, [r12 + BC_CAND]
mov [r12 + BC_LAST_PSEUDO], rax
inc qword [r12 + BC_PSEUDO]
lock inc qword [TOT_PSEUDO]
mov [TOT_LAST_PSEUDO], rax
.bs_advance:
add qword [r12 + BC_CAND], 8
pop r15
pop r14
pop r13
pop r12
pop r11
pop r10
pop r9
pop r8
pop rsi
pop rdi
pop rdx
pop rcx
pop rbx
pop rax
ret
; =============================================================================
; trial_div_r14: R14 = 1 if n (RDI) has a factor in [3,101], else 0
; =============================================================================
trial_div_r14:
push rax
push rbx
push rcx
push rdx
xor r14, r14
mov rcx, rdi
cmp rcx, 4
jb .td_done
test rcx, 1
jz .td_yes
mov rbx, 3
.td_loop:
mov rax, rbx
mul rax
cmp rcx, rax
jb .td_done
mov rax, rcx
xor rdx, rdx
div rbx
test rdx, rdx
jz .td_yes
add rbx, 2
cmp rbx, 103
jb .td_loop
jmp .td_done
.td_yes:
mov r14, 1
.td_done:
pop rdx
pop rcx
pop rbx
pop rax
ret
; =============================================================================
; save_resume: write candidate positions to 0x9F000
; =============================================================================
save_resume:
push rax
push rbx
; Write four candidates
mov rax, [BPSW_C0 + BC_CAND]
mov [RESUME_BASE + RESUME_OFF_C0], rax
mov rbx, rax
mov rax, [BPSW_C1 + BC_CAND]
mov [RESUME_BASE + RESUME_OFF_C1], rax
xor rbx, rax
mov rax, [BPSW_C2 + BC_CAND]
mov [RESUME_BASE + RESUME_OFF_C2], rax
xor rbx, rax
mov rax, [BPSW_C3 + BC_CAND]
mov [RESUME_BASE + RESUME_OFF_C3], rax
xor rbx, rax
mov [RESUME_BASE + RESUME_OFF_CKSUM], rbx
mov rax, RESUME_MAGIC
mov [RESUME_BASE + RESUME_OFF_MAGIC], rax
mov qword [RESUME_BASE + RESUME_OFF_VER], 3
pop rbx
pop rax
ret
; =============================================================================
; update_rate: compute candidates/sec (RDTSC-based), store in TOT_RATE_RAW
; rate_raw = (delta_tested << 20) / delta_tsc_hi20
; At 3GHz: delta_tsc for 1s ≈ 3e9; >>20 ≈ 2861. rate_raw * 2861 ≈ cands/sec.
; Displayed as rate_raw in hex; human-readable conversion in vga_update.
; =============================================================================
update_rate:
push rax
push rbx
push rcx
push rdx
rdtsc
shl rdx, 32
or rax, rdx ; current TSC
mov rbx, [TOT_TSC_PREV]
test rbx, rbx
jz .rate_init
sub rax, rbx ; delta_tsc
jz .rate_done
mov rcx, [TOT_TESTED]
sub rcx, [TOT_TEST_PREV] ; delta_tested
shl rcx, 20 ; << 20
mov rbx, rax
shr rbx, 20 ; delta_tsc >> 20
jz .rate_done
xor rdx, rdx
mov rax, rcx
div rbx
mov [TOT_RATE_RAW], rax
.rate_init:
.rate_done:
rdtsc
shl rdx, 32
or rax, rdx
mov [TOT_TSC_PREV], rax
mov rax, [TOT_TESTED]
mov [TOT_TEST_PREV], rax
pop rdx
pop rcx
pop rbx
pop rax
ret
; ============================================================================
; SERIAL OUTPUT HELPERS (COM1, 115200 8N1)
; ============================================================================
; serial_wait: wait for TX empty
serial_wait:
push rax
push rdx
.w:
mov dx, 0x3FD
in al, dx
and al, 0x20
jz .w
pop rdx
pop rax
ret
; serial_putchar: send AL via COM1
serial_putchar:
push rdx
push rax
mov ah, al
call serial_wait
mov dx, 0x3F8
mov al, ah
out dx, al
pop rax
pop rdx
ret
serial_putchar_L:
mov al, 'L'
jmp serial_putchar
serial_putchar_V:
mov al, 'V'
jmp serial_putchar
; serial_put_hex64: print RAX as 16 hex digits + newline to COM1
serial_put_hex64:
push rcx
push rax
push rbx
mov rbx, rax
mov rcx, 16
.hex:
mov rax, rbx
shr rax, 60
and eax, 0x0F
movzx eax, byte [hex_digits + PHYS_ADJ + rax]
call serial_putchar
shl rbx, 4
loop .hex
; newline
mov al, 0x0D
call serial_putchar
mov al, 0x0A
call serial_putchar
pop rbx
pop rax
pop rcx
ret
; serial_puts: RSI = physical address of null-terminated string
serial_puts:
push rsi
push rax
.next:
lodsb
test al, al
jz .done
call serial_putchar
jmp .next
.done:
pop rax
pop rsi
ret
; =============================================================================
; AP TRAMPOLINE (16-bit, copied to 0x8000)
; =============================================================================
; =============================================================================
; vga_pct_decimal: display RAX (= percent * 10^8) as "X.XXXXXXXX%"
; RDI = VGA destination
; =============================================================================
vga_pct_decimal:
push rax
push rbx
push rcx
push rdx
push rdi
; Split: whole = rax / 10^8, frac = rax mod 10^8
mov rbx, 100000000 ; 10^8
xor rdx, rdx
div rbx
; rax = whole part (0 or small number), rdx = frac
; Write whole digit(s) — at most 2 digits (max ~54% before 2^64)
push rdx ; save frac
; Convert whole to digits
cmp rax, 10
jb .one_digit
; two digits
mov rbx, rax
xor rdx, rdx
mov rcx, 10
div rcx
add al, '0'
mov [rdi], al
mov byte [rdi+1], 0x0F
add rdi, 2
mov rax, rdx
.one_digit:
add al, '0'
mov [rdi], al
mov byte [rdi+1], 0x0F
add rdi, 2
; Decimal point
mov byte [rdi], '.'
mov byte [rdi+1], 0x0F
add rdi, 2
; 8 fractional digits
pop rax ; frac
mov rcx, 8
.frac_loop:
mov rbx, 10
xor rdx, rdx
; shift left: multiply frac by 10, extract top digit
; frac is < 10^8; multiply by 10 fits in 64 bits (< 10^9 < 2^30)
mov rbx, 10000000 ; 10^7
xor rdx, rdx
div rbx
; rax = digit, rdx = remainder
add al, '0'
mov [rdi], al
mov byte [rdi+1], 0x0F
add rdi, 2
mov rax, rdx
; scale: new frac = old_frac mod 10^(8-digit)
; just continue with rdx and loop
; but we divided by 10^7 each time — wrong. Use simpler approach:
; extract each decimal digit by multiply-by-10
dec rcx
jz .frac_done
; Recompute: multiply remaining by 10
; rdx is now the remainder after extracting one digit
; We need to repeat with rbx = 10^(7-1) = 10^6 etc.
; Simpler: just shift the original fraction
; Actually just use modulo approach properly below
jmp .frac_loop
.frac_done:
; "%" suffix
mov byte [rdi], '%'
mov byte [rdi+1], 0x0A ; green
add rdi, 2
pop rdi
pop rdx
pop rcx
pop rbx
pop rax
ret
; =============================================================================
; vga_hours_days: display RAX (hours) as "XXXd XXh XXm" at RDI
; =============================================================================
vga_hours_days:
push rax
push rbx
push rcx
push rdx
push rdi
; days = hours / 24, rem_hours = hours mod 24
mov rbx, 24
xor rdx, rdx
div rbx
push rdx ; save rem_hours
; rax = days
; display days (up to 6 digits)
call vga_dec32_3d ; display rax as up to 4 chars
mov byte [rdi], 'd'
mov byte [rdi+1], 0x0F
add rdi, 2
mov byte [rdi], ' '
mov byte [rdi+1], 0x0F
add rdi, 2
; hours
pop rax ; rem_hours
push rax
call vga_dec32_2d
mov byte [rdi], 'h'
mov byte [rdi+1], 0x0F
add rdi, 2
mov byte [rdi], ' '
mov byte [rdi+1], 0x0F
add rdi, 2
; minutes: not tracked, show --
mov byte [rdi], '-'
mov byte [rdi+1], 0x07
add rdi, 2
mov byte [rdi], '-'
mov byte [rdi+1], 0x07
add rdi, 2
mov byte [rdi], 'm'
mov byte [rdi+1], 0x07
pop rax
pop rdi
pop rdx
pop rcx
pop rbx
pop rax
ret
; vga_dec32_3d: display RAX as up to 4 decimal digits at RDI, advance RDI
vga_dec32_3d:
push rax
push rbx
push rcx
push rdx
; up to 9999 days meaningful; just display up to 4 digits
mov rcx, 4
; find leading digit by dividing repeatedly
mov rbx, 1000
jmp .d3_start
.d3_loop:
xor rdx, rdx
div rbx
add al, '0'
mov [rdi], al
mov byte [rdi+1], 0x0F
add rdi, 2
mov rax, rdx
xor rdx, rdx
mov rax, rbx
mov rbx, 10
div rbx
mov rbx, rax
mov rax, rdx
.d3_start:
dec rcx
jnz .d3_loop
; last digit
add al, '0'
mov [rdi], al
mov byte [rdi+1], 0x0F
add rdi, 2
pop rdx
pop rcx
pop rbx
pop rax
ret
; vga_dec32_2d: display RAX (< 100) as 2 decimal digits at RDI
vga_dec32_2d:
push rax
push rdx
mov rbx, 10
xor rdx, rdx
div rbx
add al, '0'
mov [rdi], al
mov byte [rdi+1], 0x0F
add rdi, 2
mov rax, rdx
add al, '0'
mov [rdi], al
mov byte [rdi+1], 0x0F
add rdi, 2
pop rdx
pop rax
ret
BITS 16
ap_trampoline:
cli
xor ax, ax
mov ds, ax
; AP alive
mov es, ax
mov ss, ax
mov sp, 0x7C00
; Use embedded GDT (don't rely on boot sector memory at 0x7C00)
lgdt [cs:ap_gdt_ptr - ap_trampoline]
mov eax, cr0
or eax, 1
mov cr0, eax
jmp dword 0x08:AP_PM_PHYS
; Embedded GDT for AP (at known offset from ap_trampoline start)
align 8
ap_gdt_base:
dq 0x0000000000000000
dq 0x00CF9A000000FFFF ; 0x08: 32-bit code
dq 0x00CF92000000FFFF ; 0x10: data
dq 0x00AF9A000000FFFF ; 0x18: 64-bit code
ap_gdt_end:
ap_gdt_ptr:
dw ap_gdt_end - ap_gdt_base - 1
dd AP_TRAMP_PHYS + (ap_gdt_base - ap_trampoline)
; AP: 32-bit pmode
BITS 32
ap_pm_entry:
mov ax, 0x10
mov ds, ax
mov es, ax
mov ss, ax
; Get APIC ID for per-AP stack assignment (in 32-bit PM)
mov eax, 1
cpuid
shr ebx, 24
and ebx, 0xFF ; ebx = APIC ID (0..3)
; Per-AP temp stack: 0x210000 + apic_id * 0x8000
mov esp, 0x210000
imul ebx, 0x8000
add esp, ebx
; PAE
mov eax, cr4
or eax, (1 << 5)
mov cr4, eax
; EFER.LME
mov ecx, 0xC0000080
rdmsr
or eax, (1 << 8)
wrmsr
; Use BSP page tables
mov eax, PML4_PHYS
mov cr3, eax
; Paging on
mov eax, cr0
or eax, (1 << 31)
mov cr0, eax
jmp dword 0x18:AP_LM_PHYS
; AP: 64-bit entry
BITS 64
ap_lm_entry:
mov ax, 0x10
mov ds, ax
mov es, ax
mov ss, ax
; APIC ID -> stack assignment
mov eax, 1
cpuid
shr ebx, 24
and ebx, 0xFF
; Private stack: AP_STACK_BASE + apic_id * AP_STACK_STRIDE
mov rcx, AP_STACK_BASE
mov rdx, rbx
imul rdx, AP_STACK_STRIDE
add rcx, rdx
mov rsp, rcx
; IMMEDIATE: set our bit in READY_MASK right here to confirm we're alive
; APIC ID 1=bit1, 2=bit2, 3=bit3 -> shift 1 by apic_id
push rcx
mov rcx, rbx
mov rax, 1
shl rax, cl ; rax = 1 << apic_id
lock or qword [READY_MASK], rax
pop rcx
; Dispatch by APIC ID
cmp ebx, 1
je .water
cmp ebx, 2
je .earth
cmp ebx, 3
je .wind
jmp .dead
.water: call role_water
jmp .dead
.earth: call role_earth
jmp .dead
.wind: call role_wind
.dead:
cli
.halt:
hlt
jmp .halt
align 2
ap_trampoline_end:
; =============================================================================
; VGA INITIALIZATION
; =============================================================================
BITS 64
vga_init:
; Clear screen (2000 cells, attribute 0x07 = white on black)
mov rdi, VGA_BASE
mov ax, 0x0720
mov rcx, 2000
rep stosw
; Row 0: title
mov rdi, VGA_BASE + VGA_ROW * 0
mov rsi, str_title + PHYS_ADJ
mov bl, 0x0F ; bright white
call vga_puts_color
; Row 1: topology
mov rdi, VGA_BASE + VGA_ROW * 1
mov rsi, str_topology + PHYS_ADJ
mov bl, 0x0B ; cyan
call vga_puts_color
; Row 2: STATE header
mov rdi, VGA_BASE + VGA_ROW * 2
mov rsi, str_state + PHYS_ADJ
mov bl, 0x07
call vga_puts_color
; Row 3: FIRE header
mov rdi, VGA_BASE + VGA_ROW * 3
mov rsi, str_fire + PHYS_ADJ
mov bl, 0x0C ; bright red
call vga_puts_color
; Row 4: WATER header
mov rdi, VGA_BASE + VGA_ROW * 4
mov rsi, str_water + PHYS_ADJ
mov bl, 0x09 ; bright blue
call vga_puts_color
; Row 5: EARTH header
mov rdi, VGA_BASE + VGA_ROW * 5
mov rsi, str_earth + PHYS_ADJ
mov bl, 0x0A ; bright green
call vga_puts_color
; Row 6: WIND header
mov rdi, VGA_BASE + VGA_ROW * 6
mov rsi, str_wind + PHYS_ADJ
mov bl, 0x0E ; yellow
call vga_puts_color
; Row 7: ORACLE header
mov rdi, VGA_BASE + VGA_ROW * 7
mov rsi, str_oracle + PHYS_ADJ
mov bl, 0x0D ; bright magenta
call vga_puts_color
; Row 8: YIN header
mov rdi, VGA_BASE + VGA_ROW * 8
mov rsi, str_yin + PHYS_ADJ
mov bl, 0x07
call vga_puts_color
; Rows 9-12: per-core BPSW
mov rdi, VGA_BASE + VGA_ROW * 9
mov rsi, str_c0 + PHYS_ADJ
mov bl, 0x0B
call vga_puts_color
mov rdi, VGA_BASE + VGA_ROW * 10
mov rsi, str_c1 + PHYS_ADJ
mov bl, 0x0B
call vga_puts_color
mov rdi, VGA_BASE + VGA_ROW * 11
mov rsi, str_c2 + PHYS_ADJ
mov bl, 0x0B
call vga_puts_color
mov rdi, VGA_BASE + VGA_ROW * 12
mov rsi, str_c3 + PHYS_ADJ
mov bl, 0x0B
call vga_puts_color
; Row 13: totals + rate
mov rdi, VGA_BASE + VGA_ROW * 13
mov rsi, str_tot + PHYS_ADJ
mov bl, 0x0E
call vga_puts_color
; Row 14: progress (bright white)
mov rdi, VGA_BASE + VGA_ROW * 14
mov rsi, str_prog + PHYS_ADJ
mov bl, 0x0F
call vga_puts_color
; Row 15: prize (bright red)
mov rdi, VGA_BASE + VGA_ROW * 15
mov rsi, str_prize + PHYS_ADJ
mov bl, 0x0C
call vga_puts_color
ret
; =============================================================================
; VGA UPDATE (called every PRINT_EVERY iterations)
; =============================================================================
; Column positions for values (each hex64 = 16 chars + 1 space = 17 cols)
; Labels end around col 10, values start at col 10 (byte offset = col*2)
vga_update:
; ── Row 2: STATE K= A= B= ──
mov rdi, VGA_BASE + VGA_ROW * 2 + 10*2
mov rax, [STATE_K]
call vga_hex64
mov rdi, VGA_BASE + VGA_ROW * 2 + 28*2
mov rax, [STATE_A]
call vga_hex64
mov rdi, VGA_BASE + VGA_ROW * 2 + 46*2
mov rax, [STATE_B]
call vga_hex64
; ── Row 3: FIRE A= B= ──
mov rdi, VGA_BASE + VGA_ROW * 3 + 10*2
mov rax, [FIRE_A]
call vga_hex64
mov rdi, VGA_BASE + VGA_ROW * 3 + 28*2
mov rax, [FIRE_B]
call vga_hex64
; ── Row 4: WATER A= B= ──
mov rdi, VGA_BASE + VGA_ROW * 4 + 10*2
mov rax, [WATER_A]
call vga_hex64
mov rdi, VGA_BASE + VGA_ROW * 4 + 28*2
mov rax, [WATER_B]
call vga_hex64
; ── Row 5: EARTH N= NF= DELTA= ──
mov rdi, VGA_BASE + VGA_ROW * 5 + 10*2
mov rax, [EARTH_N]
call vga_hex64
mov rdi, VGA_BASE + VGA_ROW * 5 + 28*2
mov rax, [EARTH_N_FIRE]
call vga_hex64
mov rdi, VGA_BASE + VGA_ROW * 5 + 46*2
mov rax, [EARTH_DELTA]
call vga_hex64
; ── Row 6: WIND RA= RB= FIX= ──
mov rdi, VGA_BASE + VGA_ROW * 6 + 10*2
mov rax, [WIND_RES_A]
call vga_hex64
mov rdi, VGA_BASE + VGA_ROW * 6 + 28*2
mov rax, [WIND_RES_B]
call vga_hex64
mov rdi, VGA_BASE + VGA_ROW * 6 + 46*2
mov rax, [WIND_FIX]
call vga_hex64
; ── Row 7: ORACLE= STRATEGY= ──
mov rdi, VGA_BASE + VGA_ROW * 7 + 10*2
mov rax, [ORACLE]
call vga_hex64
mov rdi, VGA_BASE + VGA_ROW * 7 + 28*2
mov rax, [STRATEGY]
call vga_hex64
; Strategy name
mov rdi, VGA_BASE + VGA_ROW * 7 + 46*2
mov rax, [STRATEGY]
call vga_strategy_name
; ── Row 8: YIN PH DEPTH ──
mov rdi, VGA_BASE + VGA_ROW * 8 + 10*2
mov rax, [YIN]
call vga_hex64
mov rdi, VGA_BASE + VGA_ROW * 8 + 28*2
mov rax, [PHASE]
call vga_hex64
mov rdi, VGA_BASE + VGA_ROW * 8 + 46*2
mov rax, [DEPTH]
call vga_hex64
; ── Update rate + save resume every display cycle ──
call update_rate
call save_resume
; ── Rows 9-12: per-core BPSW status ──
; C0: N= TST= S2= BPSW=
mov rdi, VGA_BASE + VGA_ROW * 9 + 5*2
mov rax, [BPSW_C0 + BC_CAND]
call vga_hex64
mov rdi, VGA_BASE + VGA_ROW * 9 + 23*2
mov rax, [BPSW_C0 + BC_TESTED]
call vga_hex64
mov rdi, VGA_BASE + VGA_ROW * 9 + 41*2
mov rax, [BPSW_C0 + BC_SPSP2]
call vga_hex64
mov rdi, VGA_BASE + VGA_ROW * 9 + 59*2
mov rax, [BPSW_C0 + BC_BPSW]
call vga_hex64
; C1
mov rdi, VGA_BASE + VGA_ROW * 10 + 5*2
mov rax, [BPSW_C1 + BC_CAND]
call vga_hex64
mov rdi, VGA_BASE + VGA_ROW * 10 + 23*2
mov rax, [BPSW_C1 + BC_TESTED]
call vga_hex64
mov rdi, VGA_BASE + VGA_ROW * 10 + 41*2
mov rax, [BPSW_C1 + BC_SPSP2]
call vga_hex64
mov rdi, VGA_BASE + VGA_ROW * 10 + 59*2
mov rax, [BPSW_C1 + BC_BPSW]
call vga_hex64
; C2
mov rdi, VGA_BASE + VGA_ROW * 11 + 5*2
mov rax, [BPSW_C2 + BC_CAND]
call vga_hex64
mov rdi, VGA_BASE + VGA_ROW * 11 + 23*2
mov rax, [BPSW_C2 + BC_TESTED]
call vga_hex64
mov rdi, VGA_BASE + VGA_ROW * 11 + 41*2
mov rax, [BPSW_C2 + BC_SPSP2]
call vga_hex64
mov rdi, VGA_BASE + VGA_ROW * 11 + 59*2
mov rax, [BPSW_C2 + BC_BPSW]
call vga_hex64
; C3
mov rdi, VGA_BASE + VGA_ROW * 12 + 5*2
mov rax, [BPSW_C3 + BC_CAND]
call vga_hex64
mov rdi, VGA_BASE + VGA_ROW * 12 + 23*2
mov rax, [BPSW_C3 + BC_TESTED]
call vga_hex64
mov rdi, VGA_BASE + VGA_ROW * 12 + 41*2
mov rax, [BPSW_C3 + BC_SPSP2]
call vga_hex64
mov rdi, VGA_BASE + VGA_ROW * 12 + 59*2
mov rax, [BPSW_C3 + BC_BPSW]
call vga_hex64
; ── Row 13: totals + rate ──
mov rdi, VGA_BASE + VGA_ROW * 13 + 7*2
mov rax, [TOT_TESTED]
call vga_hex64
mov rdi, VGA_BASE + VGA_ROW * 13 + 25*2
mov rax, [TOT_SPSP2]
call vga_hex64
mov rdi, VGA_BASE + VGA_ROW * 13 + 43*2
mov rax, [TOT_BPSW]
call vga_hex64
mov rdi, VGA_BASE + VGA_ROW * 13 + 59*2
mov rax, [TOT_RATE_RAW]
call vga_hex64
; ── Row 14: human-readable progress ──
; Percent = TOT_TESTED / 2^63 * 100
; = (TOT_TESTED * 100) >> 63
; Use upper bits to avoid overflow: (TOT_TESTED >> 33) * 100 / 2^30
; = (TOT_TESTED >> 33) * 100 >> 30
; Result is in units of 10^-8 percent. Format as X.XXXXXXXX%
mov rax, [TOT_TESTED]
shr rax, 33 ; rax = TOT_TESTED / 2^33
mov rbx, 100
mul rbx ; rdx:rax = rax * 100
shr rax, 30 ; rax = percent * 10^8 (8 decimal places)
; Display as decimal: split into whole and fractional parts
; whole = rax / 10^8, frac = rax mod 10^8
mov rdi, VGA_BASE + VGA_ROW * 14 + 10*2
call vga_pct_decimal ; display rax as X.XXXXXXXX%
; ETA: hours to cover 2^64
; remaining = 2^64 - TOT_TESTED (approx: just use 2^64 for simplicity since tiny)
; rate_cands_sec = TOT_RATE_RAW * 2861 (approx at 3GHz; good enough for ETA)
; hours = 2^64 / rate_cands_sec / 3600
; = 2^64 / (TOT_RATE_RAW * 2861 * 3600)
; To keep in range: hours = (2^64 >> 20) / (TOT_RATE_RAW * (2861*3600 >> 20))
; = 2^44 / (TOT_RATE_RAW * 10325) approx
mov rax, [TOT_RATE_RAW]
test rax, rax
jz .no_eta
mov rbx, 10325 ; 2861 * 3600 / 1000 (scaled to avoid overflow)
mul rbx ; rdx:rax = rate * 10325
test rdx, rdx
jnz .eta_overflow ; rate too low to compute meaningfully
; hours = (1 << 44) / rax
mov rbx, rax
mov rax, 1
shl rax, 44
xor rdx, rdx
div rbx ; rax = hours remaining (approx)
mov rdi, VGA_BASE + VGA_ROW * 14 + 32*2
call vga_hours_days ; display as Xd Xh Xm
jmp .eta_done
.eta_overflow:
.no_eta:
.eta_done:
; ── Row 15: prize row ──
mov rdi, VGA_BASE + VGA_ROW * 15 + 12*2
mov rax, [TOT_PSEUDO]
call vga_hex64
mov rdi, VGA_BASE + VGA_ROW * 15 + 31*2
mov rax, [TOT_LAST_PSEUDO]
call vga_hex64
; ── COM1 serial: send terse status line ──
; Format: "D=xxxx O=xx S=x\r\n"
call serial_putchar_V ; 'V' = VGA update marker
mov rsi, str_serial_depth + PHYS_ADJ
call serial_puts
mov rax, [DEPTH]
call serial_put_hex64
mov rsi, str_serial_oracle + PHYS_ADJ
call serial_puts
mov rax, [ORACLE]
call serial_put_hex64
ret
; ============================================================================
; vga_strategy_name: print strategy name at RDI
; Input: rax = STRATEGY index
; ============================================================================
vga_strategy_name:
cmp rax, STRATEGY_FLOWING
je .flowing
cmp rax, STRATEGY_NONACTION
je .nonaction
cmp rax, STRATEGY_REDIRECT
je .redirect
cmp rax, STRATEGY_CONVERGE
je .converge
cmp rax, STRATEGY_CRITICAL
je .critical
mov rsi, str_strat_unknown + PHYS_ADJ
jmp .print
.flowing:
mov rsi, str_strat_flowing + PHYS_ADJ
jmp .print
.nonaction:
mov rsi, str_strat_nonaction + PHYS_ADJ
jmp .print
.redirect:
mov rsi, str_strat_redirect + PHYS_ADJ
jmp .print
.converge:
mov rsi, str_strat_converge + PHYS_ADJ
jmp .print
.critical:
mov rsi, str_strat_critical + PHYS_ADJ
.print:
mov bl, 0x0D
jmp vga_puts_color ; tail call
; =============================================================================
; VGA HELPERS
; =============================================================================
; vga_puts_color: RDI=dest, RSI=string, BL=attribute
vga_puts_color:
.next:
lodsb
test al, al
jz .done
mov [rdi], al
mov [rdi + 1], bl
add rdi, 2
jmp .next
.done:
ret
; vga_hex64: RDI=dest, RAX=value, writes 16 hex digits
vga_hex64:
push rbx
push rcx
push rdx
push rdi
push rax
mov rcx, 16
mov rbx, rdi
.hloop:
mov rdx, rax
shr rdx, 60
and edx, 0x0F
movzx edx, byte [hex_digits + PHYS_ADJ + rdx]
mov [rbx], dl
mov byte [rbx + 1], 0x07
add rbx, 2
shl rax, 4
loop .hloop
pop rax
pop rdi
pop rdx
pop rcx
pop rbx
ret
; =============================================================================
; STRINGS
; =============================================================================
str_title:
db "HDGL Z[phi] WU-WEI SUBSTRATE FIRE/WATER/EARTH/WIND",0
str_topology:
db "CPU0=FIRE CPU1=WATER CPU2=EARTH CPU3=WIND",0
str_state:
db "STATE K= A= B=",0
str_fire:
db "FIRE A= B=",0
str_water:
db "WATER A= B=",0
str_earth:
db "EARTH N= NF= DELTA=",0
str_wind:
db "WIND RA= RB= FIX=",0
str_oracle:
db "ORACLE= STRATEGY=",0
str_yin:
db "YIN S= PH= DEPTH=",0
str_c0: db "C0: N= TST= S2= BPSW=",0
str_c1: db "C1: N= TST= S2= BPSW=",0
str_c2: db "C2: N= TST= S2= BPSW=",0
str_c3: db "C3: N= TST= S2= BPSW=",0
str_tot: db "TOT: N= S2= BPSW= RATE=",0
str_prog: db "COVERAGE: % ETA: (0=resuming, FF..=done)",0
str_prize: db "PSEUDOPRIME: LAST= *** $620! ***",0
str_strat_flowing: db "FLOWING RIVER",0
str_strat_nonaction: db "NON-ACTION ",0
str_strat_redirect: db "REDIRECT ",0
str_strat_converge: db "CONVERGENCE ",0
str_strat_critical: db "!! CRITICAL !!",0
str_strat_unknown: db "UNKNOWN ",0
hex_digits:
db "0123456789ABCDEF"
str_serial_depth: db "DEPTH=",0
str_serial_oracle: db "ORACLE=",0
; =============================================================================
; PHYSICAL ADDRESS CONSTANTS
; =============================================================================
;
; All labels are relative to ORG 0x7C00.
; Physical address of a label L in payload = 0x10000 + (L - boot_start) - 512
; because:
; - payload loads at physical 0x10000
; - boot_start = 0x7C00
; - sector 1 (boot sector) = 512 bytes, payload starts at file offset 512
; - So physical(L) = 0x10000 + (L - 0x7C00) - 512
; = 0x10000 + L - 0x7E00
; = L + (0x10000 - 0x7E00)
; = L + 0x8200
;
; Verify: protected_entry label value = 0x7C00 + 512 = 0x7E00
; physical = 0x7E00 + 0x8200 = 0x10200. Correct!
;
; For AP trampoline (copied to 0x8000):
; ap_trampoline label = 0x7C00 + (its file offset)
; AP_PM_PHYS = 0x8000 + (ap_pm_entry - ap_trampoline)
; AP_LM_PHYS = 0x8000 + (ap_lm_entry - ap_trampoline)
PROTECTED_ENTRY_PHYS equ protected_entry
LONG_MODE_ENTRY_PHYS equ long_mode_entry
AP_PM_PHYS equ AP_TRAMP_PHYS + (ap_pm_entry - ap_trampoline)
AP_LM_PHYS equ AP_TRAMP_PHYS + (ap_lm_entry - ap_trampoline)
; =============================================================================
; IMAGE PADDING TO EXACTLY 64 SECTORS
; =============================================================================
times (IMAGE_SECTORS * 512) - ($ - $$) db 0
Five Gates ($2000 Prize B)
Premise:
https://arxiv.org/pdf/2006.14425
iris2-combined.zip (10.5 KB)
.asm
; =============================================================================
; HDGL — COMBINED PRIMALITY ORACLE
; Stronger than the Baillie-PSW enhanced test (Baillie, Fiori, Wagstaff 2020)
; =============================================================================
;
; TARGET: x86-64 / BIOS / QEMU (or real hardware)
; BUILD: nasm -f bin combined.asm -o combined.img
; RUN: qemu-system-x86_64 -drive format=raw,file=combined.img -m 128M -boot c
;
; FIVE GATES — A composite pseudoprime must survive all five simultaneously:
;
; Gate 1 spsp(2) Strong pseudoprime base 2 [Miller-Rabin]
; Gate 2 spsp(3) Strong pseudoprime base 3 [Miller-Rabin]
; Gate 3 Frobenius phi^N = expected in Z[phi]/(N), D=5 fixed
; Encodes F_N ≡ ±1 AND F_{N-1} ≡ 1 (mod N) jointly
; Implies U_{N+1}=0 AND V_{N+1}=2Q for D=5/Q=-1
; Gate 4 slpsp Strong Lucas probable prime, adaptive Selfridge D
; U_d ≡ 0 (mod N) OR V_{d·2^r} ≡ 0 for some 0≤r<s
; Gate 5 vpsp V_{N+1} ≡ 2Q (mod N) with the same adaptive D/Q
;
; Gate 3 (iris1's Frobenius) imposes a JOINT non-strong constraint in one fixed
; ring. Gates 4+5 impose adaptive strong constraints in a second ring chosen
; to avoid Q≡±1. Gates 1+2 are independent Fermat witnesses.
;
; No composite is known to survive even Gate 1 + Gate 4 (original BPSW).
; The paper's enhanced test adds Gate 5 and a minor Euler-Q check.
; This test adds Gate 2 and Gate 3 on top of the paper's enhanced test.
;
; DISPLAY:
; Row 0 title
; Row 1 N=<candidate> TESTED=<count>
; Row 2 [G1:MR2] [G2:MR3] [G3:FRB] [G4:SLC] [G5:VPS]
; Row 3 SELFRIDGE D= Q= s= d=
; Row 4 LUCAS U_d= V_d=
; Row 5 PASS COUNT= LAST=
; Row 6 PSEUDOPRIME= LAST= *** HOLY GRAIL ***
; Row 7 PHI-LATTICE K= N_phi=
;
; =============================================================================
BITS 16
ORG 0x7C00
; =============================================================================
; MEMORY MAP
; =============================================================================
AP_TRAMP_PHYS equ 0x00020000
PML4_PHYS equ 0x00021000
PDPT_PHYS equ 0x00022000
PD0_PHYS equ 0x00023000
PD1_PHYS equ 0x00024000
PD2_PHYS equ 0x00025000
PD3_PHYS equ 0x00026000
BSP_STACK equ 0x00070000
VGA_BASE equ 0x000B8000
VGA_ROW equ 160
IMAGE_SECTORS equ 64
PAYLOAD_SECTORS equ IMAGE_SECTORS - 1
PHYS_ADJ equ 0
; Phi-lattice substrate
STATE_A equ 0x00500000
STATE_B equ 0x00500008
STATE_K equ 0x00500010
EARTH_N equ 0x00500020
; Oracle state
ORA_CANDIDATE equ 0x00501000 ; N being tested
ORA_TESTED equ 0x00501008 ; total candidates tested
ORA_PASS_COUNT equ 0x00501010 ; probable-prime count
ORA_LAST_PASS equ 0x00501018 ; most recent probable prime
ORA_PSEUDO_COUNT equ 0x00501020 ; composites passing all gates (the grail)
ORA_LAST_PSEUDO equ 0x00501028 ; most recent pseudoprime
ORA_G1_MR2 equ 0x00501030 ; gate results (1=pass, 0=fail)
ORA_G2_MR3 equ 0x00501038
ORA_G3_FRB equ 0x00501040 ; Frobenius Z[phi]
ORA_G4_SLC equ 0x00501048 ; strong Lucas
ORA_G5_VPS equ 0x00501050 ; vpsp V_{n+1}=2Q
ORA_SEL_D equ 0x00501060 ; Selfridge D
ORA_SEL_Q equ 0x00501068 ; Selfridge Q
ORA_SEL_S equ 0x00501070 ; s
ORA_SEL_DODD equ 0x00501078 ; d (odd part of n+1)
ORA_LUCAS_U equ 0x00501080 ; U_d
ORA_LUCAS_V equ 0x00501088 ; V_d
ORA_IS_COMPOSITE equ 0x00501090 ; 1 if trial-div confirmed composite
; Lucas computation scratch — all intermediate values live here
LS_U equ 0x00502000
LS_V equ 0x00502008
LS_QK equ 0x00502010
LS_U2 equ 0x00502018
LS_V2 equ 0x00502020
LS_N equ 0x00502028
LS_D equ 0x00502030 ; D mod N (non-negative)
LS_Q equ 0x00502038 ; Q mod N (non-negative)
LS_INV2 equ 0x00502040 ; (N+1)/2 mod N
LS_S equ 0x00502048 ; s
LS_DODD equ 0x00502050 ; d
LS_UD equ 0x00502058 ; U_d saved for vpsp restart
LS_VD equ 0x00502060 ; V_d saved
LS_QKD equ 0x00502068 ; Qk^d saved
; Frobenius scratch
FS_RA equ 0x00503000 ; result phi-coeff
FS_RB equ 0x00503008 ; result const-coeff
FS_BA equ 0x00503010 ; base phi-coeff
FS_BB equ 0x00503018 ; base const-coeff
FS_N equ 0x00503020 ; modulus
; How many substrate ticks between primality tests
ORACLE_STRIDE equ 512
PROTECTED_ENTRY_PHYS equ protected_entry
LONG_MODE_ENTRY_PHYS equ long_mode_entry
AP_PM_PHYS equ AP_TRAMP_PHYS + (ap_pm_entry - ap_trampoline)
AP_LM_PHYS equ AP_TRAMP_PHYS + (ap_lm_entry - ap_trampoline)
; =============================================================================
; BIOS BOOT
; =============================================================================
boot_start:
cli
xor ax, ax
mov ds, ax
mov es, ax
mov ss, ax
mov sp, 0x7C00
mov [boot_drive], dl
mov ax, 0x0003
int 0x10
mov ah, 0x0E
mov al, 'B'
xor bh, bh
int 0x10
mov ah, 0x41
mov bx, 0x55AA
mov dl, [boot_drive]
int 0x13
jc .use_chs
cmp bx, 0xAA55
jne .use_chs
test cl, 1
jz .use_chs
mov si, disk_packet
mov dl, [boot_drive]
mov ah, 0x42
int 0x13
jnc .disk_ok
.use_chs:
mov ax, 0x07E0
mov es, ax
xor bx, bx
mov ah, 0x02
mov al, PAYLOAD_SECTORS
mov ch, 0
mov cl, 2
mov dh, 0
mov dl, [boot_drive]
int 0x13
jc .halt
.disk_ok:
mov ax, 0x2401
int 0x15
in al, 0x92
or al, 2
and al, 0xFE
out 0x92, al
lgdt [gdt_ptr]
mov eax, cr0
or eax, 1
mov cr0, eax
jmp dword 0x08:PROTECTED_ENTRY_PHYS
.halt:
cli
hlt
disk_packet:
db 0x10, 0x00
dw PAYLOAD_SECTORS
dw 0x0000
dw 0x07E0
dq 1
boot_drive: db 0
align 8
gdt_base:
dq 0x0000000000000000
dq 0x00CF9A000000FFFF
dq 0x00CF92000000FFFF
dq 0x00AF9A000000FFFF
gdt_end:
gdt_ptr:
dw gdt_end - gdt_base - 1
dd gdt_base
times 510 - ($ - $$) db 0
dw 0xAA55
; =============================================================================
; 32-BIT PROTECTED MODE
; =============================================================================
BITS 32
protected_entry:
cli
mov ax, 0x10
mov ds, ax
mov es, ax
mov ss, ax
mov esp, BSP_STACK
mov byte [0xB8000 + 24*160], 'C'
mov byte [0xB8000 + 24*160 + 1], 0x4F
call build_page_tables
mov eax, cr4
or eax, (1 << 5)
mov cr4, eax
mov ecx, 0xC0000080
rdmsr
or eax, (1 << 8)
wrmsr
mov eax, PML4_PHYS
mov cr3, eax
mov eax, cr0
or eax, (1 << 31)
mov cr0, eax
jmp dword 0x18:LONG_MODE_ENTRY_PHYS
build_page_tables:
pushad
mov edi, PML4_PHYS
xor eax, eax
mov ecx, 0x6000 / 4
cld
rep stosd
mov dword [PML4_PHYS + 0], PDPT_PHYS | 0x003
mov dword [PML4_PHYS + 4], 0
mov dword [PDPT_PHYS + 0], PD0_PHYS | 0x003
mov dword [PDPT_PHYS + 4], 0
mov dword [PDPT_PHYS + 8], PD1_PHYS | 0x003
mov dword [PDPT_PHYS + 12], 0
mov dword [PDPT_PHYS + 16], PD2_PHYS | 0x003
mov dword [PDPT_PHYS + 20], 0
mov dword [PDPT_PHYS + 24], PD3_PHYS | 0x003
mov dword [PDPT_PHYS + 28], 0
mov edi, PD0_PHYS
xor eax, eax
mov ecx, 512
.pd0: mov edx, eax
or edx, 0x83
mov [edi], edx
mov dword [edi+4], 0
add eax, 0x200000
add edi, 8
loop .pd0
mov edi, PD1_PHYS
mov eax, 0x40000000
mov ecx, 512
.pd1: mov edx, eax
or edx, 0x83
mov [edi], edx
mov dword [edi+4], 0
add eax, 0x200000
add edi, 8
loop .pd1
mov edi, PD2_PHYS
mov eax, 0x80000000
mov ecx, 512
.pd2: mov edx, eax
or edx, 0x83
mov [edi], edx
mov dword [edi+4], 0
add eax, 0x200000
add edi, 8
loop .pd2
mov edi, PD3_PHYS
mov eax, 0xC0000000
mov ecx, 512
.pd3: mov edx, eax
or edx, 0x83
mov [edi], edx
mov dword [edi+4], 0
add eax, 0x200000
add edi, 8
loop .pd3
popad
ret
; =============================================================================
; 64-BIT ENTRY
; =============================================================================
BITS 64
long_mode_entry:
cli
mov ax, 0x10
mov ds, ax
mov es, ax
mov ss, ax
mov rsp, BSP_STACK
; Zero all state regions
mov rdi, 0x00500000
xor rax, rax
mov rcx, 0x4000 / 8
rep stosq
; Phi-lattice seed
mov qword [STATE_A], 0
mov qword [STATE_B], 1
mov qword [STATE_K], 0
mov qword [EARTH_N], 1
; Start at N=3 (odd, skip 2)
mov qword [ORA_CANDIDATE], 3
call vga_init
jmp main_loop
; =============================================================================
; MAIN LOOP — phi-lattice substrate clock
; =============================================================================
main_loop:
; Phi step: (a,b) -> (a+b, a)
mov r8, [STATE_A]
mov r9, [STATE_B]
mov rax, r8
add rax, r9
mov [STATE_A], rax
mov [STATE_B], r8
inc qword [STATE_K]
; N_phi(a,b) = -a^2 + ab + b^2
mov rax, r8
imul rax, r8
neg rax
mov rbx, r8
imul rbx, r9
add rax, rbx
mov rbx, r9
imul rbx, r9
add rax, rbx
mov [EARTH_N], rax
; Oracle: stride-gated
mov rax, [STATE_K]
test rax, (ORACLE_STRIDE - 1)
jnz .skip
call oracle_step
.skip:
; Display every 2^20 ticks
mov rax, [STATE_K]
test rax, 0xFFFFF
jnz main_loop
call vga_update
jmp main_loop
; =============================================================================
; MODMUL64: (RAX * RBX) mod RCX -> RAX
; Both inputs must be < RCX; product < RCX^2 < 2^128; DIV never faults.
; =============================================================================
modmul64:
push rdx
mul rbx
div rcx
mov rax, rdx
pop rdx
ret
; =============================================================================
; POW_MOD_INT: base^exp mod m -> RAX
; RDI=base RSI=exp RDX=modulus
; =============================================================================
pow_mod_int:
push rbx
push rcx
push r8
push r9
push r10
mov r8, rdi
mov r9, rsi
mov r10, rdx
mov rax, 1
.loop:
test r9, r9
jz .done
test r9, 1
jz .sq
mov rbx, r8
mov rcx, r10
call modmul64
.sq:
push rax
mov rax, r8
mov rbx, r8
mov rcx, r10
call modmul64
mov r8, rax
pop rax
shr r9, 1
jmp .loop
.done:
pop r10
pop r9
pop r8
pop rcx
pop rbx
ret
; =============================================================================
; MILLER_RABIN: strong pseudoprime test
; RDI=n RBX=base -> RAX=1(pass) or 0(fail)
; =============================================================================
miller_rabin:
push rbx
push rcx
push rdx
push r8
push r9
push r10
push r11
mov r10, rdi
mov r11, rbx
; n-1 = 2^s * d
mov r8, r10
dec r8
xor r9, r9
mov rcx, r8
.find_sd:
test rcx, 1
jnz .sd_done
shr rcx, 1
inc r9
jmp .find_sd
.sd_done:
; x = base^d mod n
mov rdi, r11
mov rsi, rcx
mov rdx, r10
call pow_mod_int
cmp rax, 1
je .pass
cmp rax, r8
je .pass
mov rdx, r9
dec rdx
jz .fail
.mr_loop:
push rdx
push r8
push r10
mov rbx, rax
mov rcx, r10
call modmul64
pop r10
pop r8
pop rdx
cmp rax, r8
je .pass
test rax, rax
jz .fail
dec rdx
jnz .mr_loop
.fail:
xor rax, rax
jmp .mr_ret
.pass:
mov rax, 1
.mr_ret:
pop r11
pop r10
pop r9
pop r8
pop rdx
pop rcx
pop rbx
ret
; =============================================================================
; JACOBI: Jacobi symbol (a|n) -> RAX in {-1, 0, +1}
; RDI=a (signed) RSI=n (odd positive)
; =============================================================================
jacobi:
push rbx
push rcx
push rdx
push r8
push r9
push r10
push r11
mov r8, rdi
mov r9, rsi
mov r10, 1
; Reduce a mod n, non-negative
mov rax, r8
cqo
mov rbx, r9
idiv rbx
mov r8, rdx
test r8, r8
jns .a_ok
add r8, r9
.a_ok:
.jloop:
test r8, r8
jz .j_zero
cmp r8, 1
je .j_return
; Strip factors of 2
xor r11, r11
.strip2:
test r8, 1
jnz .stripped
shr r8, 1
inc r11
jmp .strip2
.stripped:
test r11, 1
jz .e_even
mov rax, r9
and rax, 7
cmp rax, 3
je .flip2
cmp rax, 5
je .flip2
jmp .e_even
.flip2:
neg r10
.e_even:
cmp r8, 1
je .j_return
; Quadratic reciprocity
mov rax, r8
and rax, 3
cmp rax, 3
jne .no_qr
mov rax, r9
and rax, 3
cmp rax, 3
jne .no_qr
neg r10
.no_qr:
; jacobi(a,n) -> jacobi(n mod a, a)
mov rax, r9
xor rdx, rdx
div r8
mov r9, r8
mov r8, rdx
jmp .jloop
.j_zero:
xor rax, rax
jmp .jdone
.j_return:
mov rax, r10
.jdone:
pop r11
pop r10
pop r9
pop r8
pop rdx
pop rcx
pop rbx
ret
; =============================================================================
; ZPOW_MOD: phi^N mod M in Z[phi], phi^2=phi+1
; RDI=N RSI=M
; Returns: FS_RA = phi-coeff, FS_RB = const-coeff
;
; Multiplication in Z[phi]: (ra*phi+rb)*(ba*phi+bb)
; phi-coeff = ra*ba + ra*bb + rb*ba
; const-coeff = ra*ba + rb*bb
; Squaring: (ba*phi+bb)^2
; phi-coeff = ba^2 + 2*ba*bb
; const-coeff = ba^2 + bb^2
; =============================================================================
zpow_mod:
push rax
push rbx
push rcx
push r8
push r9
push r10
push r11
push r12
mov r12, rsi ; M
; result = phi^0 = 1 = 0*phi + 1
mov qword [FS_RA], 0
mov qword [FS_RB], 1
; base = phi = 1*phi + 0
mov qword [FS_BA], 1
mov qword [FS_BB], 0
mov [FS_N], r12
mov r8, rdi ; exponent N
.zp_loop:
test r8, r8
jz .zp_done
test r8, 1
jz .zp_sq
; result = result * base
; phi-coeff: ra*ba + ra*bb + rb*ba
mov rax, [FS_RA]
mov rbx, [FS_BA]
mov rcx, r12
call modmul64
mov r9, rax ; ra*ba
mov rax, [FS_RA]
mov rbx, [FS_BB]
mov rcx, r12
call modmul64
mov r10, rax ; ra*bb
mov rax, [FS_RB]
mov rbx, [FS_BA]
mov rcx, r12
call modmul64
mov r11, rax ; rb*ba
; new phi-coeff = (ra*ba + ra*bb + rb*ba) mod M
mov rax, r9
add rax, r10
cmp rax, r12
jb .rc1
sub rax, r12
.rc1:
add rax, r11
cmp rax, r12
jb .rc2
sub rax, r12
.rc2:
push rax ; save new phi-coeff
; const-coeff: ra*ba + rb*bb
mov rax, [FS_RB]
mov rbx, [FS_BB]
mov rcx, r12
call modmul64
add rax, r9 ; + ra*ba (already in r9)
cmp rax, r12
jb .rc3
sub rax, r12
.rc3:
mov [FS_RB], rax
pop rax
mov [FS_RA], rax
.zp_sq:
; base = base^2
; phi-coeff: ba^2 + 2*ba*bb
mov rax, [FS_BA]
mov rbx, [FS_BA]
mov rcx, r12
call modmul64
mov r9, rax ; ba^2
mov rax, [FS_BA]
mov rbx, [FS_BB]
mov rcx, r12
call modmul64
; 2*ba*bb mod M
add rax, rax
cmp rax, r12
jb .bsq1
sub rax, r12
.bsq1:
; new phi-coeff = ba^2 + 2*ba*bb
add rax, r9
cmp rax, r12
jb .bsq2
sub rax, r12
.bsq2:
push rax ; save new base phi-coeff
; const-coeff: ba^2 + bb^2
mov rax, [FS_BB]
mov rbx, [FS_BB]
mov rcx, r12
call modmul64 ; bb^2
add rax, r9 ; + ba^2
cmp rax, r12
jb .bsq3
sub rax, r12
.bsq3:
mov [FS_BB], rax
pop rax
mov [FS_BA], rax
shr r8, 1
jmp .zp_loop
.zp_done:
pop r12
pop r11
pop r10
pop r9
pop r8
pop rcx
pop rbx
pop rax
ret
; =============================================================================
; GATE 3 — FROBENIUS Z[phi]: phi^N = expected in Z[phi]/(N)
; RDI = N
; Returns RAX = 1 (pass) or 0 (fail)
; D=5 fixed. P=1, Q=-1. Jacobi(5,N) determines expected target.
; Split (5|N)=+1: expect phi
; Inert (5|N)=-1: expect beta = (N-1)*phi + 1
; Ramified (5|N)=0: pass only if N=5
; =============================================================================
frobenius_test:
push rbx
push rcx
push rdx
push r8
push r9
mov r8, rdi ; N
; Jacobi(5, N)
mov rdi, 5
mov rsi, r8
call jacobi
mov r9, rax ; legendre symbol
; Compute phi^N mod N
mov rdi, r8
mov rsi, r8
call zpow_mod
; FS_RA = phi-coeff, FS_RB = const-coeff
cmp r9, 0
je .ramified
cmp r9, 1
je .split
; Inert: expect (N-1, 1)
mov rax, r8
dec rax ; N-1
cmp [FS_RA], rax
jne .frob_fail
cmp qword [FS_RB], 1
jne .frob_fail
jmp .frob_pass
.split:
; Split: expect (1, 0)
cmp qword [FS_RA], 1
jne .frob_fail
cmp qword [FS_RB], 0
jne .frob_fail
jmp .frob_pass
.ramified:
; (5|N)=0 only when 5|N; only N=5 itself is prime
cmp r8, 5
je .frob_pass
jmp .frob_fail
.frob_fail:
xor rax, rax
jmp .frob_done
.frob_pass:
mov rax, 1
.frob_done:
pop r9
pop r8
pop rdx
pop rcx
pop rbx
ret
; =============================================================================
; SELFRIDGE_SELECT: Selfridge Method A
; RDI = N
; Stores D -> ORA_SEL_D, Q -> ORA_SEL_Q
; RAX = 0 ok, 1 perfect square, 2 composite (gcd found)
; =============================================================================
selfridge_select:
push rbx
push rcx
push rdx
push r8
push r9
mov r8, rdi ; N
mov r9, 5 ; |D|
mov rcx, 0 ; sign: 0=positive, 1=negative
.sel_loop:
mov rax, r9
test rcx, rcx
jz .d_pos
neg rax
.d_pos:
mov [ORA_SEL_D], rax
mov rdi, rax
mov rsi, r8
call jacobi
cmp rax, -1
je .found
cmp rax, 0
je .j_zero
add r9, 2
xor rcx, 1
cmp r9, 10000
jb .sel_loop
mov rax, 1
jmp .sel_done
.j_zero:
cmp r8, r9
je .found
mov rax, 2
jmp .sel_done
.found:
mov rdx, [ORA_SEL_D]
mov rax, 1
sub rax, rdx
sar rax, 2
mov [ORA_SEL_Q], rax
xor rax, rax
.sel_done:
pop r9
pop r8
pop rdx
pop rcx
pop rbx
ret
; =============================================================================
; STRONG_LUCAS: gates 4+5 — strong Lucas + vpsp
; RDI = N
; Reads D, Q from ORA_SEL_D, ORA_SEL_Q
; Returns RAX = 1 (both gates pass) or 0 (fail)
; Stores ORA_G4_SLC, ORA_G5_VPS, ORA_LUCAS_U, ORA_LUCAS_V
; =============================================================================
strong_lucas:
push rbx
push rcx
push rdx
push r8
push r9
push r10
push r11
push r12
push r13
push r14
push r15
mov r15, rdi ; N
mov [LS_N], r15
; Reduce D mod N, non-negative
mov rax, [ORA_SEL_D]
cqo
mov rbx, r15
idiv rbx
mov rax, rdx
test rax, rax
jns .d_ok
add rax, r15
.d_ok:
mov [LS_D], rax
; Reduce Q mod N, non-negative
mov rax, [ORA_SEL_Q]
cqo
mov rbx, r15
idiv rbx
mov rax, rdx
test rax, rax
jns .q_ok
add rax, r15
.q_ok:
mov [LS_Q], rax
; inv2 = (N+1)/2 mod N
mov rax, r15
inc rax
shr rax, 1
mov [LS_INV2], rax
; m = N+1 = 2^s * d (d odd)
mov rax, r15
inc rax
xor r8, r8
.strip_m:
test rax, 1
jnz .stripped_m
shr rax, 1
inc r8
jmp .strip_m
.stripped_m:
mov [LS_S], r8
mov [LS_DODD], rax
mov [ORA_SEL_S], r8
mov [ORA_SEL_DODD], rax
; Init (U, V, Qk) = (0, 2, 1)
xor rax, rax
mov [LS_U], rax
mov rax, 2
mov [LS_V], rax
mov rax, 1
mov [LS_QK], rax
; Fast doubling over all bits of d (MSB first)
mov rax, [LS_DODD]
bsr r9, rax ; r9 = MSB index
.bit_loop:
; DOUBLE
mov rax, [LS_U]
mov rbx, [LS_V]
mov rcx, [LS_N]
call modmul64
mov [LS_U2], rax ; U*V = new U
mov rax, [LS_V]
mov rbx, [LS_V]
mov rcx, [LS_N]
call modmul64 ; V^2
mov rbx, [LS_QK]
add rbx, rbx
cmp rbx, [LS_N]
jb .qk2_ok
sub rbx, [LS_N]
.qk2_ok:
sub rax, rbx
jns .v2k_ok
add rax, [LS_N]
.v2k_ok:
mov [LS_V2], rax ; V^2 - 2*Qk = new V
mov rax, [LS_QK]
mov rbx, [LS_QK]
mov rcx, [LS_N]
call modmul64 ; Qk^2 = new Qk
mov rbx, [LS_U2]
mov [LS_U], rbx
mov rbx, [LS_V2]
mov [LS_V], rbx
mov [LS_QK], rax
; ADD if bit r9 of d is 1
mov rax, [LS_DODD]
mov rcx, r9
shr rax, cl
test rax, 1
jz .no_add
; Save U_old for V computation
mov rax, [LS_U]
mov [LS_U2], rax
; U_new = (1*U + V) * inv2 mod N (P=1)
add rax, [LS_V]
cmp rax, [LS_N]
jb .u_add_ok
sub rax, [LS_N]
.u_add_ok:
mov rbx, [LS_INV2]
mov rcx, [LS_N]
call modmul64
mov [LS_U], rax
; V_new = (D*U_old + 1*V) * inv2 mod N
mov rax, [LS_D]
mov rbx, [LS_U2]
mov rcx, [LS_N]
call modmul64 ; D*U_old
add rax, [LS_V]
cmp rax, [LS_N]
jb .v_add_ok
sub rax, [LS_N]
.v_add_ok:
mov rbx, [LS_INV2]
mov rcx, [LS_N]
call modmul64
mov [LS_V], rax
; Qk_new = Qk * Q
mov rax, [LS_QK]
mov rbx, [LS_Q]
mov rcx, [LS_N]
call modmul64
mov [LS_QK], rax
.no_add:
test r9, r9
jz .doubling_done
dec r9
jmp .bit_loop
.doubling_done:
; Save (U_d, V_d, Q^d) — needed for vpsp restart
mov rax, [LS_U]
mov [LS_UD], rax
mov [ORA_LUCAS_U], rax
mov rbx, [LS_V]
mov [LS_VD], rbx
mov [ORA_LUCAS_V], rbx
mov rcx, [LS_QK]
mov [LS_QKD], rcx
; ── GATE 4: STRONG LUCAS ──
; Pass if U_d=0 (mod N) or V_d=0 (mod N) [r=0]
; or V_{d*2^r}=0 for r=1..s-1
test rax, rax
jz .g4_pass
test rbx, rbx
jz .g4_pass
mov r8, [LS_S]
test r8, r8
jz .g4_fail
.vloop:
dec r8
jz .g4_fail
; V = V^2 - 2*Qk (using current Qk, BEFORE squaring it)
mov rax, [LS_V]
mov rbx, [LS_V]
mov rcx, [LS_N]
call modmul64
mov rbx, [LS_QK]
add rbx, rbx
cmp rbx, [LS_N]
jb .v2a
sub rbx, [LS_N]
.v2a:
sub rax, rbx
jns .v2b
add rax, [LS_N]
.v2b:
mov [LS_V], rax
test rax, rax
jz .g4_pass
; Now square Qk
mov rax, [LS_QK]
mov rbx, [LS_QK]
mov rcx, [LS_N]
call modmul64
mov [LS_QK], rax
jmp .vloop
.g4_fail:
mov qword [ORA_G4_SLC], 0
mov qword [ORA_G5_VPS], 0
xor rax, rax
jmp .sl_done
.g4_pass:
mov qword [ORA_G4_SLC], 1
; ── GATE 5: VPSP — V_{N+1} = 2Q ──
; Restart from (U_d, V_d, Q^d), do s more doublings
mov rax, [LS_UD]
mov [LS_U], rax
mov rax, [LS_VD]
mov [LS_V], rax
mov rax, [LS_QKD]
mov [LS_QK], rax
mov r8, [LS_S]
.vpsp_loop:
test r8, r8
jz .vpsp_check
mov rax, [LS_U]
mov rbx, [LS_V]
mov rcx, [LS_N]
call modmul64
mov [LS_U2], rax
mov rax, [LS_V]
mov rbx, [LS_V]
mov rcx, [LS_N]
call modmul64
mov rbx, [LS_QK]
add rbx, rbx
cmp rbx, [LS_N]
jb .vp1
sub rbx, [LS_N]
.vp1:
sub rax, rbx
jns .vp2
add rax, [LS_N]
.vp2:
mov [LS_V2], rax
mov rax, [LS_QK]
mov rbx, [LS_QK]
mov rcx, [LS_N]
call modmul64
mov [LS_QK], rax
mov rax, [LS_U2]
mov [LS_U], rax
mov rax, [LS_V2]
mov [LS_V], rax
dec r8
jmp .vpsp_loop
.vpsp_check:
; Compare V_{N+1} with 2Q mod N
mov rax, [LS_Q]
add rax, rax
cmp rax, [LS_N]
jb .twoQ_ok
sub rax, [LS_N]
.twoQ_ok:
cmp rax, [LS_V]
jne .g5_fail
mov qword [ORA_G5_VPS], 1
mov rax, 1
jmp .sl_done
.g5_fail:
mov qword [ORA_G5_VPS], 0
xor rax, rax
.sl_done:
pop r15
pop r14
pop r13
pop r12
pop r11
pop r10
pop r9
pop r8
pop rdx
pop rcx
pop rbx
ret
; =============================================================================
; IS_COMPOSITE_BY_TRIAL: trial division to ~100
; RDI = N -> RAX = 1 composite, 0 inconclusive
; =============================================================================
is_composite_by_trial:
push rbx
push rcx
push rdx
mov rcx, rdi
cmp rcx, 2
jb .yes
cmp rcx, 3
jbe .no
test rcx, 1
jz .yes
mov rbx, 3
.loop:
mov rax, rbx
mul rax
cmp rcx, rax
jb .no
mov rax, rcx
xor rdx, rdx
div rbx
test rdx, rdx
jz .yes
add rbx, 2
cmp rbx, 101
jb .loop
.no:
xor rax, rax
jmp .td_done
.yes:
mov rax, 1
.td_done:
pop rdx
pop rcx
pop rbx
ret
; =============================================================================
; ORACLE_STEP: run all five gates on current ORA_CANDIDATE, advance
; =============================================================================
oracle_step:
push rax
push rbx
push rcx
push rdx
push rdi
push rsi
push r8
push r9
push r10
push r11
push r12
push r13
push r14
push r15
mov rax, [ORA_CANDIDATE]
cmp rax, 5
jb .advance
; Skip even
test rax, 1
jz .advance
; Clear gate results
mov qword [ORA_G1_MR2], 0
mov qword [ORA_G2_MR3], 0
mov qword [ORA_G3_FRB], 0
mov qword [ORA_G4_SLC], 0
mov qword [ORA_G5_VPS], 0
; Trial division: is it provably composite?
mov rdi, [ORA_CANDIDATE]
call is_composite_by_trial
mov [ORA_IS_COMPOSITE], rax
inc qword [ORA_TESTED]
; ── Gate 1: MR base 2 ──
mov rdi, [ORA_CANDIDATE]
mov rbx, 2
call miller_rabin
mov [ORA_G1_MR2], rax
test rax, rax
jz .advance ; failed gate 1
; ── Gate 2: MR base 3 ──
mov rdi, [ORA_CANDIDATE]
mov rbx, 3
call miller_rabin
mov [ORA_G2_MR3], rax
test rax, rax
jz .advance ; failed gate 2
; ── Gate 3: Frobenius Z[phi] ──
mov rdi, [ORA_CANDIDATE]
call frobenius_test
mov [ORA_G3_FRB], rax
test rax, rax
jz .advance ; failed gate 3
; ── Selfridge parameter selection ──
mov rdi, [ORA_CANDIDATE]
call selfridge_select
test rax, rax
jnz .advance ; composite detected by gcd
; ── Gates 4+5: Strong Lucas + vpsp ──
mov rdi, [ORA_CANDIDATE]
call strong_lucas
test rax, rax
jz .advance ; failed gate 4 or 5
; ── All five gates passed ──
inc qword [ORA_PASS_COUNT]
mov rax, [ORA_CANDIDATE]
mov [ORA_LAST_PASS], rax
; Check if trial division confirmed composite
cmp qword [ORA_IS_COMPOSITE], 1
jne .not_pseudoprime
; !!! COMPOSITE THAT PASSES ALL FIVE GATES !!!
inc qword [ORA_PSEUDO_COUNT]
mov rax, [ORA_CANDIDATE]
mov [ORA_LAST_PSEUDO], rax
.not_pseudoprime:
.advance:
add qword [ORA_CANDIDATE], 2
pop r15
pop r14
pop r13
pop r12
pop r11
pop r10
pop r9
pop r8
pop rsi
pop rdi
pop rdx
pop rcx
pop rbx
pop rax
ret
; =============================================================================
; VGA
; =============================================================================
vga_init:
mov rdi, VGA_BASE + VGA_ROW * 0
mov rsi, str_title + PHYS_ADJ
mov bl, 0x0B
call vga_puts_color
mov rdi, VGA_BASE + VGA_ROW * 1
mov rsi, str_row1 + PHYS_ADJ
mov bl, 0x07
call vga_puts_color
mov rdi, VGA_BASE + VGA_ROW * 2
mov rsi, str_row2 + PHYS_ADJ
mov bl, 0x07
call vga_puts_color
mov rdi, VGA_BASE + VGA_ROW * 3
mov rsi, str_row3 + PHYS_ADJ
mov bl, 0x07
call vga_puts_color
mov rdi, VGA_BASE + VGA_ROW * 4
mov rsi, str_row4 + PHYS_ADJ
mov bl, 0x07
call vga_puts_color
mov rdi, VGA_BASE + VGA_ROW * 5
mov rsi, str_row5 + PHYS_ADJ
mov bl, 0x07
call vga_puts_color
mov rdi, VGA_BASE + VGA_ROW * 6
mov rsi, str_row6 + PHYS_ADJ
mov bl, 0x0C
call vga_puts_color
mov rdi, VGA_BASE + VGA_ROW * 7
mov rsi, str_row7 + PHYS_ADJ
mov bl, 0x08
call vga_puts_color
ret
vga_update:
; Row 1: N= TESTED=
mov rdi, VGA_BASE + VGA_ROW * 1 + 2*2
mov rax, [ORA_CANDIDATE]
call vga_hex64
mov rdi, VGA_BASE + VGA_ROW * 1 + 22*2
mov rax, [ORA_TESTED]
call vga_hex64
; Row 2: gate results
mov rdi, VGA_BASE + VGA_ROW * 2 + 4*2
mov rax, [ORA_G1_MR2]
call vga_gate
mov rdi, VGA_BASE + VGA_ROW * 2 + 12*2
mov rax, [ORA_G2_MR3]
call vga_gate
mov rdi, VGA_BASE + VGA_ROW * 2 + 20*2
mov rax, [ORA_G3_FRB]
call vga_gate
mov rdi, VGA_BASE + VGA_ROW * 2 + 28*2
mov rax, [ORA_G4_SLC]
call vga_gate
mov rdi, VGA_BASE + VGA_ROW * 2 + 36*2
mov rax, [ORA_G5_VPS]
call vga_gate
; Row 3: Selfridge params
mov rdi, VGA_BASE + VGA_ROW * 3 + 2*2
mov rax, [ORA_SEL_D]
call vga_hex64
mov rdi, VGA_BASE + VGA_ROW * 3 + 20*2
mov rax, [ORA_SEL_Q]
call vga_hex64
mov rdi, VGA_BASE + VGA_ROW * 3 + 36*2
mov rax, [ORA_SEL_S]
call vga_hex64
mov rdi, VGA_BASE + VGA_ROW * 3 + 50*2
mov rax, [ORA_SEL_DODD]
call vga_hex64
; Row 4: Lucas U_d, V_d
mov rdi, VGA_BASE + VGA_ROW * 4 + 4*2
mov rax, [ORA_LUCAS_U]
call vga_hex64
mov rdi, VGA_BASE + VGA_ROW * 4 + 22*2
mov rax, [ORA_LUCAS_V]
call vga_hex64
; Row 5: pass count, last pass
mov rdi, VGA_BASE + VGA_ROW * 5 + 16*2
mov rax, [ORA_PASS_COUNT]
call vga_hex64
mov rdi, VGA_BASE + VGA_ROW * 5 + 39*2
mov rax, [ORA_LAST_PASS]
call vga_hex64
; Row 6: pseudoprime count, last (holy grail)
mov rdi, VGA_BASE + VGA_ROW * 6 + 14*2
mov rax, [ORA_PSEUDO_COUNT]
call vga_hex64
mov rdi, VGA_BASE + VGA_ROW * 6 + 33*2
mov rax, [ORA_LAST_PSEUDO]
call vga_hex64
; Row 7: phi-lattice
mov rdi, VGA_BASE + VGA_ROW * 7 + 14*2
mov rax, [STATE_K]
call vga_hex64
mov rdi, VGA_BASE + VGA_ROW * 7 + 32*2
mov rax, [EARTH_N]
call vga_hex64
ret
; vga_gate: print "PASS" (green) or "FAIL" (red) at RDI, RAX=result
vga_gate:
test rax, rax
jz .fail
push rsi
mov rsi, str_pass + PHYS_ADJ
mov bl, 0x0A
call vga_puts_color
pop rsi
ret
.fail:
push rsi
mov rsi, str_fail + PHYS_ADJ
mov bl, 0x0C
call vga_puts_color
pop rsi
ret
vga_puts_color:
.next:
lodsb
test al, al
jz .done
mov [rdi], al
mov [rdi+1], bl
add rdi, 2
jmp .next
.done:
ret
vga_hex64:
push rbx
push rcx
push rdi
push rax
mov rcx, 16
mov rbx, rdi
.h:
mov rdx, rax
shr rdx, 60
and edx, 0x0F
movzx edx, byte [hex_digits + PHYS_ADJ + rdx]
mov [rbx], dl
mov byte [rbx+1], 0x07
add rbx, 2
shl rax, 4
loop .h
pop rax
pop rdi
pop rcx
pop rbx
ret
; =============================================================================
; STRINGS
; =============================================================================
str_title:
db "HDGL COMBINED ORACLE 5-GATE PRIMALITY TEST Z[phi]+BPSW", 0
str_row1:
db "N= TESTED=", 0
str_row2:
db "G1: G2: G3: G4: G5:", 0
str_row3:
db "D= Q= s= d=", 0
str_row4:
db "U_d= V_d=", 0
str_row5:
db "PROB-PRIME COUNT= LAST=", 0
str_row6:
db "PSEUDOPRIME COUNT= LAST= !GRAIL!", 0
str_row7:
db "PHI-LATTICE K= N_phi=", 0
str_pass: db "PASS", 0
str_fail: db "FAIL", 0
hex_digits: db "0123456789ABCDEF"
; =============================================================================
; AP TRAMPOLINE
; =============================================================================
BITS 16
ap_trampoline:
cli
xor ax, ax
mov ds, ax
mov es, ax
mov ss, ax
mov sp, 0x7C00
lgdt [cs:ap_gdt_ptr - ap_trampoline]
mov eax, cr0
or eax, 1
mov cr0, eax
jmp dword 0x08:AP_PM_PHYS
align 8
ap_gdt_base:
dq 0x0000000000000000
dq 0x00CF9A000000FFFF
dq 0x00CF92000000FFFF
dq 0x00AF9A000000FFFF
ap_gdt_end:
ap_gdt_ptr:
dw ap_gdt_end - ap_gdt_base - 1
dd AP_TRAMP_PHYS + (ap_gdt_base - ap_trampoline)
BITS 32
ap_pm_entry:
mov ax, 0x10
mov ds, ax
mov es, ax
mov ss, ax
mov esp, 0x00078000
mov eax, cr4
or eax, (1 << 5)
mov cr4, eax
mov ecx, 0xC0000080
rdmsr
or eax, (1 << 8)
wrmsr
mov eax, PML4_PHYS
mov cr3, eax
mov eax, cr0
or eax, (1 << 31)
mov cr0, eax
jmp dword 0x18:AP_LM_PHYS
BITS 64
ap_lm_entry:
.halt: cli
hlt
jmp .halt
ap_trampoline_end:
times (IMAGE_SECTORS * 512) - ($ - $$) db 0



