From 194e5b742dc45b170017c460814aebb358cf9dfe Mon Sep 17 00:00:00 2001 From: Nestor Soriano Date: Tue, 10 Feb 2026 21:45:53 +0100 Subject: [PATCH 1/4] Implement a boot menu activated by pressing N at boot time. This menu allows to disable Nextor 3 kernels before they initialize, and also allows to simulate the pressing of various boot keys. Also define a mandatory "NXT3" signature at address 407Bh for all the NExtor 3 kernels. --- source/kernel/Makefile | 18 +- source/kernel/bank0/bdos.mac | 2 +- source/kernel/bank0/betainfo.mac | 2 +- source/kernel/bank0/doshead.mac | 2 + source/kernel/bank0/init.mac | 194 +-- source/kernel/bank4/bootmenu.mac | 1385 +++++++++++++++++ source/kernel/bank4/bootmsg.mac | 209 +++ source/kernel/bank4/jump.mac | 3 + source/kernel/bank4/partit.mac | 2 +- source/kernel/condasm.inc | 1 - source/kernel/data.mac | 3 + .../drivers/StandaloneASCII8/driver.mac | 9 +- source/kernel/drv.mac | 8 +- source/kernel/kvar.mac | 1 + source/kernel/macros.inc | 4 + 15 files changed, 1739 insertions(+), 104 deletions(-) create mode 100644 source/kernel/bank4/bootmenu.mac create mode 100644 source/kernel/bank4/bootmsg.mac diff --git a/source/kernel/Makefile b/source/kernel/Makefile index ea392680..37974112 100644 --- a/source/kernel/Makefile +++ b/source/kernel/Makefile @@ -185,7 +185,7 @@ drivers/StandaloneASCII8/Nextor-$(VERSION).StandaloneASCII8.ROM: \ 256.bytes cat 256.bytes drivers/StandaloneASCII8/driver.bin > drivers/StandaloneASCII8/_driver.bin - $(MKNEXROM) nextor_base.dat $@ /d:drivers/StandaloneASCII8/_driver.bin /m:drivers/StandaloneASCII8/chgbnk.bin + $(MKNEXROM) nextor_base.dat $@ /d:drivers/StandaloneASCII8/driver.bin /m:drivers/StandaloneASCII8/chgbnk.bin $(call copy_to_bin,$@) drivers/StandaloneASCII8/driver.bin: \ @@ -353,7 +353,7 @@ B0RELS = $(addprefix bank0/,doshead.rel 40ff.rel b0.rel init.rel alloc.rel dskba B1RELS = $(addprefix bank1/,dosinit.rel mapinit.rel msg.rel) B2RELS = $(addprefix bank2/,kinit.rel char.rel dev.rel kbios.rel misc.rel seg.rel path.rel find.rel dir.rel handles.rel del.rel rw.rel files.rel buf.rel fat.rel val.rel err.rel) B3RELS = $(addprefix bank3/,dos1ker.rel) -B4RELS = $(addprefix bank4/,jump.rel env.rel cpm.rel ramdrv.rel time.rel seg.rel misc.rel dskab.rel) +B4RELS = $(addprefix bank4/,jump.rel env.rel cpm.rel ramdrv.rel time.rel seg.rel misc.rel dskab.rel bootmenu.rel) B5RELS = $(addprefix bank5/,b5.rel) B6RELS = $(addprefix bank6/,b6.rel) @@ -490,6 +490,15 @@ bank4/partit.rel: \ $(call assemble,bank4/partit.mac) +bank4/bootmsg.rel: \ + macros.inc \ + const.inc \ + condasm.inc \ + bank4/bootmsg.mac \ + bank0/betainfo.mac + + $(call assemble,bank4/bootmsg.mac) + bank4/ramdrvh.rel: \ macros.inc \ const.inc \ @@ -510,6 +519,7 @@ bank4/B4.BIN bank4/B4.SYM: \ $(COMRELS) \ $(B4RELS) \ bank4/partit.rel \ + bank4/bootmsg.rel \ bank4/b4.rel \ bank4/ramdrv.rel \ bank0/b0labels.inc \ @@ -517,8 +527,8 @@ bank4/B4.BIN bank4/B4.SYM: \ $(call print_linking,"bank 4") @$(LK80) --working-dir bank4 --symbols-file B4.SYM --output-file B4.BIN \ - --code 40FFh ../codes.rel ../kvar.rel ../data.rel b4.rel jump.rel env.rel cpm.rel partit.rel ramdrv.rel time.rel seg.rel misc.rel \ - --code 7BC0h dskab.rel \ + --code 40FFh ../codes.rel ../kvar.rel ../data.rel b4.rel jump.rel env.rel cpm.rel partit.rel ramdrv.rel time.rel seg.rel misc.rel bootmenu.rel bootmsg.rel \ + --code 7E80h dskab.rel \ --code 7fd0h ../chgbnk.rel diff --git a/source/kernel/bank0/bdos.mac b/source/kernel/bank0/bdos.mac index 7a2a0cc8..4eb299c4 100644 --- a/source/kernel/bank0/bdos.mac +++ b/source/kernel/bank0/bdos.mac @@ -188,7 +188,7 @@ DOS1_GO:: ; ; Note: KABJ and KDERJ need to be defined at the same address as defined at drv.mac -KABJ equ 7BC0h +KABJ equ 7E80h KDERJ equ KABJ+7 ld hl,KABJ ; ;Setup the disk vector and diff --git a/source/kernel/bank0/betainfo.mac b/source/kernel/bank0/betainfo.mac index 785caad2..dde5fa70 100644 --- a/source/kernel/bank0/betainfo.mac +++ b/source/kernel/bank0/betainfo.mac @@ -1,5 +1,5 @@ if ALPHA gt 0 - defb " alpha ",ALPHA+"0",ALPHAL," " + defb " alpha ",ALPHA+"0"," " endif if BETA gt 0 diff --git a/source/kernel/bank0/doshead.mac b/source/kernel/bank0/doshead.mac index b66d9b8f..5982b906 100644 --- a/source/kernel/bank0/doshead.mac +++ b/source/kernel/bank0/doshead.mac @@ -124,6 +124,8 @@ BASDEV: ld ix,DRIVER.BASDEV## ; jp BIOS_IN## ;Version 1 compatible $IN entry +NXT3_SIGNATURE:: + db "NXT3",0 ;407Bh - Signature for Nextor 3.0+ ROMs defs ROMST+80h-$,0FFh ; RAMENT:: ; RAM DISK DRIVER ENTRY POINTS diff --git a/source/kernel/bank0/init.mac b/source/kernel/bank0/init.mac index ba952578..0e93a69a 100644 --- a/source/kernel/bank0/init.mac +++ b/source/kernel/bank0/init.mac @@ -224,10 +224,6 @@ _GO_BIOS: ;For unexpanded slots, the key for subslot 0 applies (U, Q, A, Z) DISABLE_KEY: - ld a,(BOOTKEYS##+2) - and 80h - ret nz ;"N" key pressed? - call GSLOT1## and 00001111b add a,a @@ -692,35 +688,32 @@ FMAKE: CALLDOS 16h ;;Create file (FCB) RDABS: CALLDOS 2Fh ;Absolute sector read ; -PRINT_INIT_KERNEL_AT_ASLOT: - ld de,STR_INIT_NEXTOR_KERNEL_AT_SLOT - call MSG_DE - call GSLOT1 - call PRSLOT - ld de,STR_COLON - call MSG_DE +; Call PRINT_KERNEL_MSG in bank 4 via C4PBK. +; Input: C = type (0=initializing, 1=skipping, 2=init OK, 3=init failed) +; B = slot number (for types 0, 1) +CALL_PRINT_KERNEL_MSG: + ld hl,PRINT_KERNEL_MSG## + ld (BK4_ADD##),hl + jp C4PBK## + +STR_NO_MEM_FOR_DRIVER: + db 13,10,"*** Can't allocate memory for driver!",13,10,10,0 - ld b,1 - ld d,37 - ld hl,BUF - ld a,DRIVER.DRVQ_GET_STRING## - call CALL_DRIVER_QUERY - ld de,BUF +; Set up text mode, beep, and print version + "Press N" message. +; Used at AFTER_BOOT_MENU for the first kernel. +INIT_SCREEN_AND_VERSION: + ld a,(MSXVER) or a - jr z,PRINT_DRIVER_NAME - cp DRIVER.QUERY_TRUNCATED_STRING## - ld de,STR_UNNAMED_DRIVER - jr nz,PRINT_DRIVER_NAME - ld de,BUF - call MSG_DE - ld de,STR_3_DOTS -PRINT_DRIVER_NAME:: - call MSG_DE - call PRT_CRLF - ld a,(LINL40) - cp 80 - ret z - jp PRT_CRLF + ld a,40 + jr z,ISAV_GOTLINLEN + ld a,80 +ISAV_GOTLINLEN: + ld (LINL40),a + call INITXT + ld a,BELL + printchar + ld c,4 + jp CALL_PRINT_KERNEL_MSG ALIGN 4720h WRABS: CALLDOS 30h ;Absolute sector write @@ -846,9 +839,11 @@ INIT: ld hl,INIT2 push hl xor a - + ;Bank switching code must go here + ALIGN 47DCh ;BSC assumed to be at this address by BOOT_MENU + BSC:: ld (6000h),a ret @@ -859,8 +854,64 @@ INIT2: call DO_KEYS_INIT + ;--- Boot menu check --- + ; + ;Show the boot menu if: + ; - Boot keys came from the keyboard (not from RAM / NEXBOOT) + ; - The N key is being pressed + ; + ;This must run BEFORE DISABLE_KEY so that if the user disables + ;this kernel in the menu, init returns before INIHOOK wipes + ;the disk hooks set up by MSX-DOS compatible kernels. + + call SCANKEYS_RAM_AVAILABLE + jr z,AFTER_BOOT_MENU ;Keys came from RAM, flag preserved + + ld a,(BOOTKEYS##+2) + and 80h + jr nz,SHOW_BOOT_MENU ;N key pressed, show menu + + ;No menu: clear the all-disabled flag + xor a + ld (ALL_KERNELS_DISABLED##),a + jr AFTER_BOOT_MENU + +SHOW_BOOT_MENU: + + ;Set up text mode for the menu display + ld a,(MSXVER) + or a + ld a,40 + jr z,BM_GOTLINLEN + ld a,80 +BM_GOTLINLEN: + ld (LINL40),a + call INITXT + + ;Run boot menu (bank 4) + ld hl,BOOT_MENU## + ld (BK4_ADD##),hl + call C4PBK## + +AFTER_BOOT_MENU: + ld a,(ALL_KERNELS_DISABLED##) + or a + jr nz,ABM_SKIP_SCREEN + ld a,(DISKID) + or a + call z,INIT_SCREEN_AND_VERSION +ABM_SKIP_SCREEN: call DISABLE_KEY - ret nz + jr z,KERNEL_NOT_SKIPPED + ld a,(ALL_KERNELS_DISABLED##) + or a + ret nz ;All disabled: return silently + call GSLT1 + ld b,a + ld c,1 ;Type 1: "Skipping Nextor kernel at slot X" + call CALL_PRINT_KERNEL_MSG + ret +KERNEL_NOT_SKIPPED: ; if 0 ;; If 0, will boot on MSX1 machine (with calendar clock problem). ld a,(VERSION) @@ -889,9 +940,6 @@ endif ; I am the first disk cartridge. ; call INITVARS - - ld a,CLS - call CHPUT ; ; I can be the master. Check if disk system can start up. ; @@ -994,9 +1042,7 @@ INIHOOK: xor 2 ld (TARGET##),a ; - ld a,BELL ;tell user scanning is over - OUTCHR - jr OVERRIDE + jr OVERRIDE_PAST_MSG ;Skip INITXT+message (already done at AFTER_BOOT_MENU) ; VER_CHK: ; @@ -1038,29 +1084,17 @@ OVERRIDE_GOTLINLEN:: ld (LINL40),a call INITXT + ld a,BELL + OUTCHR + + ld c,4 ;Type 4: version + "Press N to show the boot menu" + call CALL_PRINT_KERNEL_MSG + +OVERRIDE_PAST_MSG: ld a,99h ;16*DOSVER##+SEC_DOS_VERSION_HIGH## ld (DOS_VER##),a ;set new DOS version ld a,16*MAIN_NEXTOR_VERSION##+SEC_NEXTOR_VERSION_HIGH## ld (NXT_VER##),a - - ;Kill any Nextor v2 kernel trying to initialize after myself. - ;We do this by using the "one time boot keys" mechanism to pretend that - ;the user used it to disable Nextor via a simulated "N" key press. - - ;TODO: This also disables other Nextor v3 kernels, do something about that. -if 0 - ld de,SCANKEYS_RAM_BASE - ld hl,RAMKEYSIG - ld bc,RAMKEYSIG_END-RAMKEYSIG - ldir - ld hl,(BOOTKEYS##) - ld (SCANKEYS_RAM_BASE+17),hl - ld hl,(BOOTKEYS##+3) - ld (SCANKEYS_RAM_BASE+17+3),hl - ld a,(BOOTKEYS##+2) - or 80h ;Simulate "N" key - ld (SCANKEYS_RAM_BASE+17+2),a -endif ; ; Set up hook for clean up procedure ; @@ -1092,7 +1126,10 @@ SLAVE: or a ret nz ;Return if four Nextor kernels initialized already - call PRINT_INIT_KERNEL_AT_ASLOT + call GSLT1 + ld b,a + ld c,0 ;Type 0: "Initializing Nextor kernel at slot X: " + call CALL_PRINT_KERNEL_MSG call VALTBL ;get number of drives declared so far ret z ;no room is left @@ -1135,8 +1172,8 @@ BOTTOM_OK: ld hl,0 jr z,GET_DRIVER_INIT_PARAMS_OK - ld de,STR_DRV_INIT_FAILED - call MSG_DE + ld c,3 ;Type 3: "*** Driver initialization failed!" + call CALL_PRINT_KERNEL_MSG jp NO_DRIVE GET_DRIVER_INIT_PARAMS_OK: @@ -1183,12 +1220,12 @@ BIGENOUGH: jr z,DRIVER_INIT_OK cp DRIVER.QUERY_NOT_IMPLEMENTED## jr z,DRIVER_INIT_OK - ld de,STR_DRV_INIT_FAILED - call MSG_DE + ld c,3 ;Type 3: "*** Driver initialization failed!" + call CALL_PRINT_KERNEL_MSG jp NO_DRIVE DRIVER_INIT_OK: - ld de,STR_DRV_INIT_OK - call MSG_DE + ld c,2 ;Type 2: "Driver initialization OK" + call CALL_PRINT_KERNEL_MSG ld hl,GET_BOOT_DRIVES_COUNT## ld (BK4_ADD##),hl @@ -1322,7 +1359,7 @@ CLEAN1: ld hl,SCANKEYS_RAM_BASE ld de,RAMKEYSIG call STRCOMP - jr nz,NO_CLEAN_SCANKEYS_RAM + jr nz,NO_CLEAN_SCANKEYS_RAM xor a ld (SCANKEYS_RAM_BASE),a NO_CLEAN_SCANKEYS_RAM: @@ -2374,9 +2411,6 @@ GSLT_SLTTBL: PRINTL INIT,%($-$$INIT) -STR_NO_MEM_FOR_DRIVER: - db 13,10,"*** Can't allocate memory for driver!",13,10,10,0 - ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; @@ -2432,9 +2466,6 @@ SETDEFDPB: jr nz,SETDEFDPB ;yes ret -STR_UNNAMED_DRIVER: - db "(Unnamed driver)",0 - ALIGN 5006h SNEXT: CALLDOS 12h ;;Search for next (FCB) ; @@ -2856,14 +2887,6 @@ endif subttl MSX-DOS1 compatible ROM entries (part 3) -STR_INIT_NEXTOR_KERNEL_AT_SLOT: - db 13,"Initializing Nextor kernel at slot ",0 -STR_COLON: - db ": ",0 -STR_3_DOTS: - db "...",0 - - ALIGN 535Dh BUFOUT: exx push bc ;Get error code in B' if it ever servives. @@ -3019,7 +3042,7 @@ GDATE: CALLDOS 2Ah ;Get date SDATE: CALLDOS 2Bh ;Set date ; - ;Scan and store boot keys and check if keyboard is Russian if that hasn't been done yet + ;Scan and store boot keys and check if keyboard is Russian if that hasn't been done yet. DO_KEYS_INIT: call CHECK_IS_RUSSIAN @@ -3028,7 +3051,7 @@ DO_KEYS_INIT: call SCANKEYS_RAM jr z,DO_KEYS_INIT2 call SCANKEYS - + ld a,(KEYS_INV_0) and 00111110b xor b @@ -3038,7 +3061,7 @@ DO_KEYS_INIT: and 00110000b xor h ld h,a - + DO_KEYS_INIT2: ld a,b ld (BOOTKEYS##),a @@ -3047,11 +3070,6 @@ DO_KEYS_INIT2: ret -STR_DRV_INIT_FAILED: - db 13,10,"*** Driver initialization failed!",13,10,10,0 -STR_DRV_INIT_OK: - db 13,10,"Driver initialization OK",13,10,10,0 - ALIGN 55DBh GTIME: CALLDOS 2Ch ;Get time ; diff --git a/source/kernel/bank4/bootmenu.mac b/source/kernel/bank4/bootmenu.mac new file mode 100644 index 00000000..ddec94ad --- /dev/null +++ b/source/kernel/bank4/bootmenu.mac @@ -0,0 +1,1385 @@ + .z80 + name('bootmenu') + title MSX-DOS 2 KERNEL Copyright (1986) IS Systems Ltd. + subttl Boot Menu +;----------------------------------------------------------------------------- +; + INCLUDE ../macros.inc + INCLUDE ../const.inc +; +;----------------------------------------------------------------------------- +; Local equates +;----------------------------------------------------------------------------- + +CALLB0: equ 403Fh +CALDRV_ADDR: equ 4048h +BSC_ADDR: equ 47DCh +NXT3_SIG_ADDR: equ 407Bh + +BUF: equ 0F55Eh +KBUF: equ 0F41Fh + +RDSLT: equ 000Ch +CALSLT: equ 001Ch +SNSMAT: equ 0141h +KILBUF: equ 0156h +POSIT: equ 00C6h + +EXPTBL: equ 0FCC1h +H_CHPH: equ 0FDA4h +MSXVER: equ 002Dh +CSRY: equ 0F3DCh +CSRX: equ 0F3DDh +CSRSW: equ 0F3CAh +PUTPNT: equ 0F3F8h +GETPNT: equ 0F3FAh +KEYBUF_ADDR: equ 0FBF0h +KEYBUF_END: equ 0FC18h ;FBF0h + 40 + +SCANKEYS_RAM_BASE: equ 0A100h + +;--- Data layout in KBUF --- + +MENU_DATA: equ KBUF +KERNEL_COUNT: equ MENU_DATA ;1 byte +LOCAL_KEYS: equ MENU_DATA+1 ;5 bytes (working copy of BOOTKEYS) +ORIG_KEYS: equ MENU_DATA+6 ;5 bytes (original BOOTKEYS for ESC) +EXIT_MODE: equ MENU_DATA+11 ;1 byte: 0=ESC, 1=ENTER, 2=N +KERNEL_ENTRIES: equ MENU_DATA+12 ;5 entries + +KERNEL_ENTRY_SIZE: equ 59 +KE_SLOT: equ 0 ;1 byte: slot address +KE_KEY: equ 1 ;1 byte: toggle key ASCII char +KE_KEYOFF: equ 2 ;1 byte: BOOTKEYS byte offset +KE_KEYMASK: equ 3 ;1 byte: BOOTKEYS bit mask +KE_FLAGS: equ 4 ;1 byte: bit 0 = enabled, bit 1 = has version, bit 7 = bad driver +KE_NAME: equ 5 ;51 bytes: driver name, zero-terminated +KE_VER_MAJOR: equ 56 ;1 byte: major version (0-255) +KE_VER_MINOR: equ 57 ;1 byte: minor version (0-255) +KE_VER_REV: equ 58 ;1 byte: revision (0-255) + +KE_FLAG_ENABLED: equ 0 +KE_FLAG_HAS_VER: equ 1 +KE_FLAG_BAD: equ 7 + +MAX_KERNELS: equ 5 + +EXIT_ESC: equ 0 +EXIT_ENTER: equ 1 +EXIT_N: equ 2 + +;--- Screen layout --- +; Line 0: title (centered) +; Line 1: ruler +; Line 2: blank +; Lines 3..3+N-1: kernel entries +; Line 3+N: "N. Disable all and boot" +; Line 3+N+1: blank +; Lines 3+N+2...: numeric key entries +; After keys: blank +; Then: ruler +; Then: footer + +MENU_FIRST_KERNEL_LINE: equ 3 +SNSMAT_ROW_N: equ 4 ;Keyboard matrix row for N key +SNSMAT_BIT_N: equ 3 ;Bit position for N key in row 4 + + +;============================================================================= +; BOOT_MENU - Main entry point +; Called from bank 0 via CALL4 (BK4_ADD mechanism) +;============================================================================= + + PROC BOOT_MENU + + ;--- Save original BOOTKEYS and make working copy --- + + ld hl,BOOTKEYS## + ld de,LOCAL_KEYS + ld bc,5 + ldir ;LOCAL_KEYS = BOOTKEYS + + ld hl,BOOTKEYS## + ld de,ORIG_KEYS + ld bc,5 + ldir ;ORIG_KEYS = BOOTKEYS (for ESC) + + ;Clear N bit in both LOCAL_KEYS and ORIG_KEYS + ld a,(LOCAL_KEYS+2) + and 01111111b + ld (LOCAL_KEYS+2),a + ld (ORIG_KEYS+2),a + + ;--- Scan slots for Nextor 3 kernels --- + + call SCAN_SLOTS + + ;--- Set all detected kernels as enabled by default --- + ;(Clear their per-slot disable bits in LOCAL_KEYS) + + ld a,(KERNEL_COUNT) + or a + jr z,BM_DISPLAY ;No kernels found (shouldn't happen) + + ld b,a + ld ix,KERNEL_ENTRIES +BM_ENABLE_LOOP: + bit KE_FLAG_BAD,(ix+KE_FLAGS) + jr nz,BM_ENABLE_BAD + + ;Enable this kernel: clear its disable bit in LOCAL_KEYS + set KE_FLAG_ENABLED,(ix+KE_FLAGS) + ld e,(ix+KE_KEYOFF) + ld d,0 + ld hl,LOCAL_KEYS + add hl,de + ld a,(ix+KE_KEYMASK) + cpl + and (hl) + ld (hl),a ;Clear the disable bit + jr BM_ENABLE_NEXT + +BM_ENABLE_BAD: + ;Bad driver: ensure disabled bit is SET in LOCAL_KEYS + res KE_FLAG_ENABLED,(ix+KE_FLAGS) + ld e,(ix+KE_KEYOFF) + ld d,0 + ld hl,LOCAL_KEYS + add hl,de + ld a,(ix+KE_KEYMASK) + or (hl) + ld (hl),a ;Set the disable bit +BM_ENABLE_NEXT: + push bc + ld bc,KERNEL_ENTRY_SIZE + add ix,bc + pop bc + djnz BM_ENABLE_LOOP + +BM_DISPLAY: + ;--- Display the menu --- + + call DISPLAY_MENU + + ;--- Hide cursor (must be AFTER display, CLS may reset CSRSW) --- + xor a + ld (CSRSW),a + + ;--- Handle user input --- + + call MENU_INPUT_LOOP + + ;--- Process exit --- + + ld a,(EXIT_MODE) + or a + jr z,BM_EXIT_ESC ;ESC: write original keys, don't update BOOTKEYS + + ;ENTER or N: write LOCAL_KEYS to RAM and BOOTKEYS + ld hl,LOCAL_KEYS + call WRITE_RAM_KEYS + + ld hl,LOCAL_KEYS + ld de,BOOTKEYS## + ld bc,5 + ldir + + ;Check if all kernels are disabled + ld a,(EXIT_MODE) + cp EXIT_N + jr z,BM_SET_ALL_DISABLED ;N always disables all + + ;For ENTER: check each kernel entry + ld a,(KERNEL_COUNT) + or a + jr z,BM_SET_ALL_DISABLED ;No kernels = all disabled + ld b,a + ld ix,KERNEL_ENTRIES +BM_CKAD_LOOP: + bit KE_FLAG_ENABLED,(ix+KE_FLAGS) + jr nz,BM_SET_NOT_ALL_DISABLED + push bc + ld bc,KERNEL_ENTRY_SIZE + add ix,bc + pop bc + djnz BM_CKAD_LOOP +BM_SET_ALL_DISABLED: + ld a,1 + jr BM_CKAD_DONE +BM_SET_NOT_ALL_DISABLED: + xor a +BM_CKAD_DONE: + ld (ALL_KERNELS_DISABLED##),a + jr BM_DONE + +BM_EXIT_ESC: + ;ESC: write ORIG_KEYS to RAM (has N cleared), don't touch BOOTKEYS + ld hl,ORIG_KEYS + call WRITE_RAM_KEYS + xor a + ld (ALL_KERNELS_DISABLED##),a + +BM_DONE: + ;Show cursor again + ld a,0FFh + ld (CSRSW),a + ;Clear screen before returning to normal boot + ld a,FF ;CLS + printchar + ret + + +;============================================================================= +; SCAN_SLOTS - Find all Nextor 3 kernels in the system +;============================================================================= + +SCAN_SLOTS: + xor a + ld (KERNEL_COUNT),a + + ;Iterate primary slots 0-3 + ld c,0 ;C = primary slot number + +SS_PRIMARY_LOOP: + ;Check if this primary slot is expanded + ld a,c + ld hl,EXPTBL + ld e,a + ld d,0 + add hl,de + bit 7,(hl) + jr nz,SS_EXPANDED + + ;Not expanded: check subslot 0 only + ld a,c ;Slot address = primary, no expansion flag + call CHECK_AND_ADD_SLOT + jr SS_PRIMARY_NEXT + +SS_EXPANDED: + ;Expanded: check subslots 0-3 + ld b,0 ;B = subslot number +SS_SUBSLOT_LOOP: + ld a,b + rlca + rlca ;A = subslot << 2 + or c ;A = primary | (subslot << 2) + or 80h ;Set expansion flag + call CHECK_AND_ADD_SLOT + ld a,(KERNEL_COUNT) + cp MAX_KERNELS + ret z ;Stop if we have 5 kernels + inc b + ld a,b + cp 4 + jr c,SS_SUBSLOT_LOOP + +SS_PRIMARY_NEXT: + ld a,(KERNEL_COUNT) + cp MAX_KERNELS + ret z + inc c + ld a,c + cp 4 + jr c,SS_PRIMARY_LOOP + ret + + +;============================================================================= +; CHECK_AND_ADD_SLOT - Check one slot and add to list if Nextor 3 +; Input: A = slot address +;============================================================================= + +CHECK_AND_ADD_SLOT: + push bc + ld b,a ;B = slot address to check + + ;--- Check for "AB" ROM signature at 4000h --- + ;Note: RDSLT clobbers AF,BC,DE,HL so we must preserve B (slot addr) + + ld a,b + ld hl,4000h + push bc + call RDSLT + pop bc + cp 'A' + jp nz,CAS_NOT_FOUND + + ld a,b + ld hl,4001h + push bc + call RDSLT + pop bc + cp 'B' + jp nz,CAS_NOT_FOUND + + ;--- Check for "NXT3" signature at 407Bh --- + + ld a,b + ld hl,NXT3_SIG_ADDR + push bc + call RDSLT + pop bc + cp 'N' + jp nz,CAS_NOT_FOUND + + ld a,b + ld hl,NXT3_SIG_ADDR+1 + push bc + call RDSLT + pop bc + cp 'X' + jp nz,CAS_NOT_FOUND + + ld a,b + ld hl,NXT3_SIG_ADDR+2 + push bc + call RDSLT + pop bc + cp 'T' + jp nz,CAS_NOT_FOUND + + ld a,b + ld hl,NXT3_SIG_ADDR+3 + push bc + call RDSLT + pop bc + cp '3' + jp nz,CAS_NOT_FOUND + + ;--- This is a Nextor 3 kernel. Add it to the list. --- + + push bc ;Save B = slot address (GET_ENTRY_PTR uses B) + ld a,(KERNEL_COUNT) + call GET_ENTRY_PTR ;IX = pointer to kernel entry + pop bc ;Restore B = slot address + + ;Store slot address + ld (ix+KE_SLOT),b + + ;Look up toggle key info from tables + ld a,b + and 0Fh ;Index into SLOT_KEY_* tables + ld e,a + ld d,0 + + ld hl,SLOT_KEY_CHARS + add hl,de + ld a,(hl) + ld (ix+KE_KEY),a + + ld hl,SLOT_KEY_OFFSET + add hl,de + ld a,(hl) + ld (ix+KE_KEYOFF),a + + ld hl,SLOT_KEY_MASK + add hl,de + ld a,(hl) + ld (ix+KE_KEYMASK),a + + ;Initialize flags (cleared, will be set by BOOT_MENU) + ld (ix+KE_FLAGS),0 + + ;--- Initialize target slot's mapper by calling BSC --- + ;This ensures CHGBNK at 7FD0h is accessible for CALDRV. + ;Critical for ASCII 8 mappers where the upper 8KB page + ;(6000h-7FFFh) may not be showing the correct ROM page + ;at power-on. BSC is in the lower 8KB, always accessible. + + push ix ;Save kernel entry pointer + xor a ;A = 0 (select bank 0) + push bc ;Save B = slot address + push bc + pop iy ;IYh = slot address for CALSLT + ld ix,BSC_ADDR + call CALSLT + ei + pop bc ;Restore B = slot address + pop ix ;Restore kernel entry pointer + + ;--- Query driver name --- + + push ix + push bc + pop iy ;IYh = slot address for CALSLT + push iy + + ;Set up BK4_ADD for CALDRV to call DRIVER_QUERY + ld hl,DRIVER.DRIVER_QUERY## + ld (BK4_ADD##),hl + + ;Calculate max name length based on screen width + ld a,(LINL40) + cp 80 + jr z,CAS_80COL + ;40-col: 40 - 5(prefix) - 7(suffix " in X-Y") - 3("...") = 25 + ld d,25 + jr CAS_GOT_NAMELEN +CAS_80COL: + ;80-col: 80 - 5(prefix) - 13(max " v255.255.255") - 12(suffix " in slot X-Y") - 3("...") = 47 + ld d,47 +CAS_GOT_NAMELEN: + + ;HL = pointer to name field in entry + push ix + pop hl + ld bc,KE_NAME + add hl,bc + + ;Call DRVQ_GET_STRING via CALSLT + CALDRV + ld a,DRIVER.DRVQ_GET_STRING## + ld b,DRIVER.DRVQ_STR_DRIVER_NAME## + ld ix,CALDRV_ADDR + pop iy ;IYh = slot address + call CALSLT + ei + + pop ix ;IX = kernel entry pointer + + ;Check result + or a + jr z,CAS_NAME_OK ;QUERY_OK + cp DRIVER.QUERY_TRUNCATED_STRING## + jr z,CAS_NAME_TRUNC + + ;Bad driver or query not supported + set KE_FLAG_BAD,(ix+KE_FLAGS) + push ix + pop hl + ld bc,KE_NAME + add hl,bc + ex de,hl ;DE = entry name field (dest) + ld hl,STR_BAD_DRIVER ;HL = source + call COPY_STRING + jr CAS_ADD_DONE + +CAS_NAME_TRUNC: + ;Append "..." to the name + push ix + pop hl + ld bc,KE_NAME + add hl,bc + call FIND_STRING_END ;HL = past end of string (at the 0 terminator) + ld (hl),'.' + inc hl + ld (hl),'.' + inc hl + ld (hl),'.' + inc hl + ld (hl),0 + +CAS_NAME_OK: + ;--- Query driver version --- + ;BK4_ADD still points to DRIVER_QUERY from the name query + + push ix ;Save entry pointer + + ld a,(ix+KE_SLOT) + ld b,a + push bc + pop iy ;IYh = slot address for CALSLT + + ld a,DRIVER.DRVQ_GET_VERSION## + ld ix,CALDRV_ADDR + call CALSLT + ei + + pop ix ;Restore entry pointer + + or a + jr nz,CAS_ADD_DONE ;Version query failed or not implemented + + ld (ix+KE_VER_MAJOR),b + ld (ix+KE_VER_MINOR),c + ld (ix+KE_VER_REV),d + set KE_FLAG_HAS_VER,(ix+KE_FLAGS) + +CAS_ADD_DONE: + ;Increment kernel count + ld hl,KERNEL_COUNT + inc (hl) + +CAS_NOT_FOUND: + pop bc + ret + + +;============================================================================= +; DISPLAY_MENU - Draw the full boot menu screen +;============================================================================= + +DISPLAY_MENU: + ld a,FF ;CLS + printchar + + ;--- Title: "Nextor X.Y.Z boot menu" --- + + call PRINT_CENTERED_TITLE + + ;--- Ruler --- + call DISPLAY_RULER + + ;--- Blank line --- + call PRINT_CRLF + + ;--- Kernel entries --- + + ld a,(KERNEL_COUNT) + or a + jr z,DM_AFTER_KERNELS + + ld b,a + ld ix,KERNEL_ENTRIES + ld c,0 ;C = entry index +DM_KERNEL_LOOP: + push bc + call DISPLAY_KERNEL_LINE + call PRINT_CRLF + ld bc,KERNEL_ENTRY_SIZE + add ix,bc + pop bc + inc c + djnz DM_KERNEL_LOOP + +DM_AFTER_KERNELS: + ;--- " N. Disable all and boot" --- + ld hl,STR_DISABLE_ALL + call PRINT_STRING + call PRINT_CRLF + + ;--- Blank line --- + call PRINT_CRLF + + ;--- Numeric key entries --- + + ld b,0 ;B = key index (0-7) +DM_KEY_LOOP: + ;Skip keys 2 and 4 on non-Turbo-R + ld a,b + cp 2 + jr z,DM_KEY_CHECK_TURBO + cp 4 + jr nz,DM_KEY_SHOW +DM_KEY_CHECK_TURBO: + ld a,(MSXVER) + cp 3 ;Turbo-R = 3 + jr c,DM_KEY_SKIP +DM_KEY_SHOW: + push bc + ld a,b + call DISPLAY_KEY_LINE + call PRINT_CRLF + pop bc +DM_KEY_SKIP: + inc b + ld a,b + cp 8 + jr c,DM_KEY_LOOP + + ;--- Blank line --- + call PRINT_CRLF + + ;--- Ruler --- + call DISPLAY_RULER + + ;--- Footer --- + ld hl,STR_FOOTER + call PRINT_STRING + ret + + +;============================================================================= +; DISPLAY_RULER - Print a line of hyphens +;============================================================================= + +DISPLAY_RULER: + ld a,(LINL40) + ld b,a + ;Korean MSX check: if H_CHPH is hooked, use width-1 + ld a,(H_CHPH) + cp 0C9h ;RET instruction = not hooked + jr z,DR_LOOP + dec b ;Korean MSX: one less hyphen +DR_LOOP: + ld a,'-' + printchar + djnz DR_LOOP + ;On Korean MSX we printed fewer chars so cursor hasn't wrapped; + ;add CRLF to move to the next line. + ;On normal MSX the full-width ruler auto-wraps, and the CRLF + ;is harmless (screen scrolls if at bottom, but we're near top). + ld a,(H_CHPH) + cp 0C9h + ret z ;Normal MSX: cursor already wrapped + jp PRINT_CRLF ;Korean MSX: end the line + + +;============================================================================= +; DISPLAY_KERNEL_LINE - Display one kernel entry +; Input: IX = kernel entry pointer +;============================================================================= + +DISPLAY_KERNEL_LINE: + ;Print "* " or " " + bit KE_FLAG_ENABLED,(ix+KE_FLAGS) + jr z,DKL_NOT_ENABLED + ld a,'*' + jr DKL_GOT_MARK +DKL_NOT_ENABLED: + ld a,' ' +DKL_GOT_MARK: + printchar + ld a,' ' + printchar + + ;Print toggle key + ". " + ld a,(ix+KE_KEY) + printchar + ld a,'.' + printchar + ld a,' ' + printchar + + ;Print driver name + push ix + pop hl + ld bc,KE_NAME + add hl,bc + call PRINT_STRING + + ;Print version in 80-col mode if available + ld a,(LINL40) + cp 80 + jr nz,DKL_NO_VERSION + bit KE_FLAG_HAS_VER,(ix+KE_FLAGS) + jr z,DKL_NO_VERSION + ld a,' ' + printchar + ld a,'v' + printchar + ld a,(ix+KE_VER_MAJOR) + call PRINT_DECIMAL + ld a,'.' + printchar + ld a,(ix+KE_VER_MINOR) + call PRINT_DECIMAL + ld a,'.' + printchar + ld a,(ix+KE_VER_REV) + call PRINT_DECIMAL +DKL_NO_VERSION: + + ;Print slot suffix + ld a,(LINL40) + cp 80 + jr z,DKL_80COL + ld hl,STR_IN_SHORT ;" in " + jr DKL_GOT_SUFFIX +DKL_80COL: + ld hl,STR_IN_SLOT ;" in slot " +DKL_GOT_SUFFIX: + call PRINT_STRING + + ;Print slot number + ld a,(ix+KE_SLOT) + call PRINT_SLOT + ret + + +;============================================================================= +; MENU_INPUT_LOOP - Handle keyboard input for the menu +;============================================================================= + +MENU_INPUT_LOOP: + ;--- Wait for N key to be released --- +MIL_WAIT_N: + ld a,SNSMAT_ROW_N + call SNSMAT + bit SNSMAT_BIT_N,a + jr z,MIL_WAIT_N ;0 = pressed, wait + + ;--- Flush keyboard buffer --- + call KILBUF + + ;--- Main input loop --- +MIL_LOOP: + call GET_KEY ;Wait for key without showing cursor + + ;Check ESC + cp ESC + jr z,MIL_ESC + + ;Check ENTER + cp CR + jr z,MIL_ENTER + + ;Check N/n + cp 'N' + jr z,MIL_N + cp 'n' + jr z,MIL_N + + ;Check digits 0-7 + cp '0' + jr c,MIL_CHECK_LETTER + cp '8' + jr nc,MIL_CHECK_LETTER + + ;Digit key pressed + sub '0' ;A = key index 0-7 + + ;Skip keys 2,4 on non-Turbo-R + cp 2 + jr z,MIL_CHECK_TURBO_KEY + cp 4 + jr nz,MIL_DO_TOGGLE_KEY +MIL_CHECK_TURBO_KEY: + push af + ld a,(MSXVER) + cp 3 + jr nc,MIL_TURBO_OK + pop af + jr MIL_LOOP ;Ignore on non-Turbo-R +MIL_TURBO_OK: + pop af + +MIL_DO_TOGGLE_KEY: + call TOGGLE_NUM_KEY + jr MIL_LOOP + +MIL_CHECK_LETTER: + ;Convert lowercase to uppercase + cp 'a' + jr c,MIL_CHECK_UPPER + cp 'z'+1 + jr nc,MIL_LOOP + sub 'a'-'A' +MIL_CHECK_UPPER: + cp 'A' + jr c,MIL_LOOP + cp 'Z'+1 + jr nc,MIL_LOOP + + ;Search kernel entries for matching toggle key + call FIND_KERNEL_BY_KEY ;Returns IX = entry, Z = found + jr nz,MIL_LOOP ;Not found + + ;Check if bad driver (can't toggle) + bit KE_FLAG_BAD,(ix+KE_FLAGS) + jr nz,MIL_LOOP + + ;Toggle this kernel + call TOGGLE_KERNEL + jr MIL_LOOP + +MIL_ESC: + ld a,EXIT_ESC + ld (EXIT_MODE),a + ret + +MIL_ENTER: + ld a,EXIT_ENTER + ld (EXIT_MODE),a + ret + +MIL_N: + ;Disable all kernels: set all per-slot disable bits + ld a,(KERNEL_COUNT) + or a + jr z,MIL_N_DONE + ld b,a + ld ix,KERNEL_ENTRIES +MIL_N_LOOP: + res KE_FLAG_ENABLED,(ix+KE_FLAGS) + ld e,(ix+KE_KEYOFF) + ld d,0 + ld hl,LOCAL_KEYS + add hl,de + ld a,(ix+KE_KEYMASK) + or (hl) + ld (hl),a ;Set the disable bit + push bc + ld bc,KERNEL_ENTRY_SIZE + add ix,bc + pop bc + djnz MIL_N_LOOP + +MIL_N_DONE: + ld a,EXIT_N + ld (EXIT_MODE),a + ret + + +;============================================================================= +; TOGGLE_KERNEL - Toggle a kernel's enabled/disabled state +; Input: IX = kernel entry pointer +; Need to find the entry index to compute display line +;============================================================================= + +TOGGLE_KERNEL: + ;Toggle the enabled flag + ld a,(ix+KE_FLAGS) + xor 1 shl KE_FLAG_ENABLED + ld (ix+KE_FLAGS),a + + ;Toggle the bit in LOCAL_KEYS + ld e,(ix+KE_KEYOFF) + ld d,0 + ld hl,LOCAL_KEYS + add hl,de + ld a,(ix+KE_KEYMASK) + xor (hl) + ld (hl),a + + ;Find this entry's index to compute line number + push ix + pop de ;DE = this entry address + ld hl,KERNEL_ENTRIES ;HL = first entry + ld a,0 ;A = index +TK_FIND_INDEX: + push hl + or a + sbc hl,de + pop hl + jr z,TK_GOT_INDEX + ld bc,KERNEL_ENTRY_SIZE + add hl,bc + inc a + cp MAX_KERNELS + jr c,TK_FIND_INDEX + ret ;Should not happen + +TK_GOT_INDEX: + ;Line number = MENU_FIRST_KERNEL_LINE + index + add a,MENU_FIRST_KERNEL_LINE+1 ;+1 for POSIT 1-based + ld l,a + ld h,1 ;Column 1 + call POSIT + + call DISPLAY_KERNEL_LINE + call CLEAR_TO_EOL + ret + + +;============================================================================= +; TOGGLE_NUM_KEY - Toggle a numeric key option +; Input: A = key index (0-7) +;============================================================================= + +TOGGLE_NUM_KEY: + push af ;Save key index + + ;Toggle bit in LOCAL_KEYS + ld e,a + ld d,0 + ld hl,KEY_BIT_TABLE + add hl,de + add hl,de ;HL = &KEY_BIT_TABLE[index*2] + ld c,(hl) ;C = BOOTKEYS byte offset + inc hl + ld b,(hl) ;B = bit mask + + ld hl,LOCAL_KEYS + ld e,c + ld d,0 + add hl,de + ld a,b + xor (hl) + ld (hl),a + + ;Compute display line number + ;First, count how many kernel entries + 2 (blank + "N. Disable all") + ;Then count how many keys 0..index-1 were displayed (skip 2,4 on non-Turbo-R) + pop af + push af + + ld c,a ;C = target key index + ld b,0 ;B = key iterator + ld d,0 ;D = count of displayed keys before target + +TNK_COUNT: + ld a,b + cp c + jr z,TNK_COUNTED + + ;Was this key displayed? + cp 2 + jr z,TNK_CHECK_TR + cp 4 + jr nz,TNK_WAS_SHOWN +TNK_CHECK_TR: + ld a,(MSXVER) + cp 3 + jr c,TNK_NEXT_COUNT ;Not shown on non-Turbo-R +TNK_WAS_SHOWN: + inc d ;Count this displayed key +TNK_NEXT_COUNT: + inc b + jr TNK_COUNT + +TNK_COUNTED: + ;D = number of displayed keys before this one + ld a,(KERNEL_COUNT) + add a,MENU_FIRST_KERNEL_LINE + add a,2 ;+1 for "N. Disable all", +1 for blank line + add a,d ;Add displayed keys before this one + inc a ;+1 for POSIT 1-based + + ld l,a + ld h,1 ;Column 1 + call POSIT + + pop af ;Recover key index + call DISPLAY_KEY_LINE + call CLEAR_TO_EOL + ret + + +;============================================================================= +; FIND_KERNEL_BY_KEY - Search kernel entries for a matching toggle key +; Input: A = uppercase key character +; Output: IX = entry pointer, Z = found, NZ = not found +;============================================================================= + +FIND_KERNEL_BY_KEY: + ld c,a ;C = key to find + ld a,(KERNEL_COUNT) + or a + jr nz,FKK_HAS_ENTRIES + inc a ;A=1, NZ: no entries, "not found" + ret + +FKK_HAS_ENTRIES: + ld b,a + ld ix,KERNEL_ENTRIES +FKK_LOOP: + ld a,(ix+KE_KEY) + cp c + ret z ;Found + push bc + ld bc,KERNEL_ENTRY_SIZE + add ix,bc + pop bc + djnz FKK_LOOP + or 1 ;Set NZ + ret + + +;============================================================================= +; WRITE_RAM_KEYS - Write boot keys to RAM at A100h +; Input: HL = pointer to 5-byte key data to write +;============================================================================= + +WRITE_RAM_KEYS: + push hl ;Save key data pointer + + ;Copy signature + ld hl,RAMKEYSIG + ld de,SCANKEYS_RAM_BASE + ld bc,RAMKEYSIG_END-RAMKEYSIG + ldir + + ;Copy key data (5 bytes) + pop hl ;Recover key data pointer + ld de,SCANKEYS_RAM_BASE+17 + ld bc,5 + ldir + ret + +RAMKEYSIG: db "NEXTOR_BOOT_KEYS",0 +RAMKEYSIG_END: + + +;============================================================================= +; DISPLAY_KEY_LINE - Display one numeric key option line +; Input: A = key index (0-7) +;============================================================================= + +DISPLAY_KEY_LINE: + push af + + ;Look up bit info + ld e,a + ld d,0 + ld hl,KEY_BIT_TABLE + add hl,de + add hl,de ;HL = KEY_BIT_TABLE + index*2 + ld c,(hl) ;C = BOOTKEYS byte offset + inc hl + ld b,(hl) ;B = bit mask + + ;Read the bit from LOCAL_KEYS + ld hl,LOCAL_KEYS + ld e,c + ld d,0 + add hl,de + ld a,(hl) + and b + + ;Print "* " or " " + jr z,DKLI_NOT_SET + ld a,'*' + jr DKLI_GOT_MARK +DKLI_NOT_SET: + ld a,' ' +DKLI_GOT_MARK: + printchar + ld a,' ' + printchar + + ;Print key number + ". " + pop af + push af + add a,'0' + printchar + ld a,'.' + printchar + ld a,' ' + printchar + + ;Print description string from lookup table + pop af + ld e,a + ld d,0 + ld hl,KEY_STR_TABLE + add hl,de + add hl,de ;HL = &KEY_STR_TABLE[index*2] + ld a,(hl) + inc hl + ld h,(hl) + ld l,a ;HL = pointer to string + call PRINT_STRING + ret + + +;============================================================================= +; Helper routines +;============================================================================= + +;--- GET_KEY: Wait for key without displaying cursor --- +; Reads directly from the keyboard buffer, bypassing CHGET which +; forcibly enables the cursor during key wait. +; Output: A = key character + +GET_KEY: + halt ;Wait for VDP interrupt (keyboard gets scanned) + ld hl,(PUTPNT) + ld de,(GETPNT) + or a ;Clear carry for sbc + sbc hl,de + jr z,GET_KEY ;Buffer empty, keep waiting + + ;Key available - read from buffer + ex de,hl ;HL = GETPNT value + ld a,(hl) ;A = key character + + ;Advance GETPNT + inc hl + push af + ld a,h + cp high KEYBUF_END + jr nz,GK_NO_WRAP + ld a,l + cp low KEYBUF_END + jr nz,GK_NO_WRAP + ld hl,KEYBUF_ADDR ;Wrap to buffer start +GK_NO_WRAP: + ld (GETPNT),hl + pop af + ret + + +;--- GET_ENTRY_PTR: Get pointer to kernel entry by index --- +; Input: A = entry index (0-4) +; Output: IX = pointer to that entry + +GET_ENTRY_PTR: + ld ix,KERNEL_ENTRIES + or a + ret z + ld b,a +GEP_LOOP: + push bc + ld bc,KERNEL_ENTRY_SIZE + add ix,bc + pop bc + djnz GEP_LOOP + ret + + +;--- PRINT_STRING: Print zero-terminated string --- +; Input: HL = string pointer + +PRINT_STRING: + ld a,(hl) + or a + ret z + printchar + inc hl + jr PRINT_STRING + + +;--- PRINT_CRLF: Print CR+LF --- + +PRINT_CRLF: + ld a,CR + printchar + ld a,LF + printchar + ret + + +;--- PRINT_DECIMAL: Print byte as 1-3 decimal digits, no leading zeros --- +; Input: A = value (0-255) + +PRINT_DECIMAL: + ld b,0 ;B = leading digit printed flag + + ;Hundreds digit + ld c,'0'-1 +PDEC_100: + inc c + sub 100 + jr nc,PDEC_100 + add a,100 ;Restore after overshoot + push af + ld a,c + cp '0' + jr z,PDEC_SKIP_100 + printchar + ld b,1 +PDEC_SKIP_100: + pop af + + ;Tens digit + ld c,'0'-1 +PDEC_10: + inc c + sub 10 + jr nc,PDEC_10 + add a,10 ;Restore after overshoot + push af + ld a,c + cp '0' + jr nz,PDEC_PRINT_10 + ld a,b + or a + jr z,PDEC_SKIP_10 + ld a,'0' +PDEC_PRINT_10: + printchar +PDEC_SKIP_10: + pop af + + ;Ones digit (always printed) + add a,'0' + printchar + ret + + +;--- PRINT_SLOT: Print slot number like "1" or "1-3" --- +; Input: A = slot address (MSX format) + +PRINT_SLOT: + push af + and 11b + add a,'0' + printchar + pop af + bit 7,a + ret z ;Not expanded + push af + ld a,'-' + printchar + pop af + rrca + rrca + and 11b + add a,'0' + printchar + ret + + +;--- PRINT_CENTERED_TITLE: Print "Nextor X.Y.Z boot menu" centered --- + +PRINT_CENTERED_TITLE: + ;Build the title string in BUF + ld hl,STR_NEXTOR + ld de,BUF + call COPY_STRING ;Copy "Nextor " + + ;Append major version digit + ld a,MAIN_NEXTOR_VERSION## + add a,'0' + ld (de),a + inc de + ld a,'.' + ld (de),a + inc de + + ;Append minor version high digit + ld a,SEC_NEXTOR_VERSION_HIGH## + add a,'0' + ld (de),a + inc de + ld a,'.' + ld (de),a + inc de + + ;Append minor version low digit + ld a,SEC_NEXTOR_VERSION_LOW## + add a,'0' + ld (de),a + inc de + + ;Append " boot menu" + push de + ld hl,STR_BOOT_MENU_SUFFIX + pop de + call COPY_STRING + + ;Calculate centering + ;String length = DE - BUF + ld hl,BUF + push hl + ex de,hl + ld de,BUF + or a + sbc hl,de ;HL = string length + ld a,l ;A = string length (assumes < 256) + ld b,a + + ld a,(LINL40) ;Screen width + sub b ;Remaining space + srl a ;Divide by 2 for centering + ld c,a ;C = number of leading spaces + + ;Print leading spaces + or a + jr z,PCT_NO_SPACES +PCT_SPACE: + ld a,' ' + printchar + dec c + jr nz,PCT_SPACE +PCT_NO_SPACES: + + ;Print the title string + pop hl + call PRINT_STRING + call PRINT_CRLF + ret + + +;--- COPY_STRING: Copy zero-terminated string --- +; Input: HL = source, DE = destination +; Output: DE = past the copied string (at the position of the 0 terminator) + +COPY_STRING: + ld a,(hl) + ld (de),a + or a + ret z + inc hl + inc de + jr COPY_STRING + + +;--- FIND_STRING_END: Find end of zero-terminated string --- +; Input: HL = string pointer +; Output: HL = pointer to the 0 terminator + +FIND_STRING_END: + ld a,(hl) + or a + ret z + inc hl + jr FIND_STRING_END + + +;--- CLEAR_TO_EOL: Print ESC+"K" sequence to clear to end of line --- + +CLEAR_TO_EOL: + ld a,ESC + printchar + ld a,'K' + printchar + ret + + +;============================================================================= +; Data tables +;============================================================================= + +;--- Slot-to-key mapping tables --- +; Indexed by (slot_address AND 0Fh) +; Index = primary_slot + subslot*4 + +SLOT_KEY_CHARS: + db 'U','Q','A','Z' ;x-0 (primary slots 0,1,2,3) + db 'I','W','S','X' ;x-1 + db 'O','E','D','C' ;x-2 + db 'P','R','F','V' ;x-3 + +SLOT_KEY_OFFSET: + db 3,3,1,4 ;x-0 + db 2,4,3,4 ;x-1 + db 3,1,1,1 ;x-2 + db 3,3,1,3 ;x-3 + +SLOT_KEY_MASK: + db 40h,04h,04h,08h ;x-0 + db 04h,01h,10h,02h ;x-1 + db 01h,40h,20h,10h ;x-2 + db 02h,08h,80h,80h ;x-3 + + +;--- Numeric key bit mapping --- +; 2 bytes per key: (BOOTKEYS byte offset, bit mask) + +KEY_BIT_TABLE: + db 0, 01h ;key 0: byte 0, bit 0 + db 0, 02h ;key 1: byte 0, bit 1 + db 0, 04h ;key 2: byte 0, bit 2 + db 0, 08h ;key 3: byte 0, bit 3 + db 0, 10h ;key 4: byte 0, bit 4 + db 0, 20h ;key 5: byte 0, bit 5 + db 4, 20h ;key 6 (CTRL): byte 4, bit 5 + db 4, 10h ;key 7 (SHIFT): byte 4, bit 4 + + +;--- Numeric key description string pointers --- + +KEY_STR_TABLE: + dw STR_KEY_0 + dw STR_KEY_1 + dw STR_KEY_2 + dw STR_KEY_3 + dw STR_KEY_4 + dw STR_KEY_5 + dw STR_KEY_6 + dw STR_KEY_7 +; |--------|---------|---------|---------| +STR_KEY_0: db "Disable permanent disk emulation",0 +STR_KEY_1: db "Boot in MSX-DOS 1 mode",0 +STR_KEY_2: db "Boot in MSX-DOS 1+R800 ROM mode",0 +STR_KEY_3: db "Boot in MSX BASIC",0 +STR_KEY_4: db "Boot in R800 ROM mode",0 +STR_KEY_5: db "Reduced drive allocation mode",0 +STR_KEY_6: db "Disable FDD ghost drives (CTRL)",0 +STR_KEY_7: db "Disable MSX-DOS kernels (SHIFT)",0 + + +;--- Menu strings --- + +STR_NEXTOR: db "Nextor ",0 +STR_BOOT_MENU_SUFFIX: db " boot menu",0 +STR_IN_SLOT: db " in slot ",0 +STR_IN_SHORT: db " in ",0 +STR_DISABLE_ALL: db " N. Disable all and boot",0 +STR_FOOTER: db " ESC = Cancel, ENTER = Apply and boot",0 +STR_BAD_DRIVER: db "(BAD DRIVER)",0 + + + finish + end diff --git a/source/kernel/bank4/bootmsg.mac b/source/kernel/bank4/bootmsg.mac new file mode 100644 index 00000000..62d98c92 --- /dev/null +++ b/source/kernel/bank4/bootmsg.mac @@ -0,0 +1,209 @@ + .z80 + name('bootmsg') + title MSX-DOS 2 KERNEL Copyright (1986) IS Systems Ltd. + subttl Boot Messages +;----------------------------------------------------------------------------- +; + INCLUDE ../macros.inc + INCLUDE ../const.inc + INCLUDE ../condasm.inc +; +;----------------------------------------------------------------------------- +; Local equates +;----------------------------------------------------------------------------- + +CALDRV_ADDR: equ 4048h +CALSLT: equ 001Ch + +BUF: equ 0F55Eh +LINL40: equ 0F3AEh + +;----------------------------------------------------------------------------- +; +; PRINT_KERNEL_MSG - Print kernel initialization status message +; Called from bank 0 via C4PBK. +; +; Input: C = type: +; 0 = "Initializing Nextor kernel at [slot ]X: " +; (40-col: no "slot", name on 2nd line; 80-col: all on one line) +; 1 = "Skipping Nextor kernel at slot X" +; 2 = "Driver initialization OK" +; 3 = "Driver initialization failed!" +; 4 = "Nextor kernel version X.Y.Z, Press N to show the boot menu" +; B = slot number (used for types 0 and 1) + + PROC PRINT_KERNEL_MSG + + ld a,c + or a + jr z,PKM_INITIALIZING + dec a + jr z,PKM_SKIPPING + dec a + jr z,PKM_INIT_OK + dec a + jr z,PKM_INIT_FAILED + ; Type 4: version + press N to show boot menu + ld de,STR_NEXTOR_VERSION + jp PKM_PRINT_STR + +PKM_INIT_FAILED: + ld de,STR_DRV_INIT_FAILED + jp PKM_PRINT_STR + +PKM_INIT_OK: + ld de,STR_DRV_INIT_OK + jp PKM_PRINT_STR + +PKM_SKIPPING: + ld de,STR_SKIPPING_NEXTOR + call PKM_PRINT_STR + call PKM_PRINT_SLOT + jr PKM_CRLF + +PKM_INITIALIZING: + push bc + ld de,STR_INITIALIZING_NEXTOR + call PKM_PRINT_STR + ld a,(LINL40) + cp 80 + jr z,PKM_INIT_80 + + ;40-col: no "slot" word, name on next line indented 4 spaces + pop bc + push bc + call PKM_PRINT_SLOT + ld a,13 + printchar + ld a,10 + printchar + ld b,4 +PKM_INIT_SPC: + ld a,' ' + printchar + djnz PKM_INIT_SPC + ld d,33 ;Max name length for 40-col + jr PKM_INIT_QUERY + +PKM_INIT_80: + ;80-col: "slot " prefix, name on same line after ": " + ld de,STR_SLOT_SPACE + call PKM_PRINT_STR + pop bc + push bc + call PKM_PRINT_SLOT + ld a,':' + printchar + ld a,' ' + printchar + ld d,32 ;Max name length for 80-col + +PKM_INIT_QUERY: + ;Query driver name via CALSLT + CALDRV + pop bc + ld hl,DRIVER.DRIVER_QUERY## + ld (BK4_ADD##),hl + push bc + pop iy ;IYh = slot for CALSLT + + ld a,DRIVER.DRVQ_GET_STRING## + ld b,DRIVER.DRVQ_STR_DRIVER_NAME## + ;D already set (33 or 32) + ld hl,BUF + ld ix,CALDRV_ADDR + call CALSLT + ei + + ;Check result + ld de,BUF + or a + jr z,PKM_PRINT_NAME + cp DRIVER.QUERY_TRUNCATED_STRING## + jr z,PKM_TRUNCATED + ld de,STR_UNNAMED_DRIVER + jr PKM_PRINT_NAME + +PKM_TRUNCATED: + ld de,BUF + call PKM_PRINT_STR + ld de,STR_3_DOTS + +PKM_PRINT_NAME: + call PKM_PRINT_STR + +PKM_CRLF: + ld a,13 + printchar + ld a,10 + printchar + ld a,(LINL40) + cp 80 + ret z + ld a,13 + printchar + ld a,10 + printchar + ret + +;--- Print null-terminated string at DE + +PKM_PRINT_STR: + ld a,(de) + or a + ret z + printchar + inc de + jr PKM_PRINT_STR + +;--- Print slot number from B +; Format: X or X-Y for expanded slots + +PKM_PRINT_SLOT: + ld a,b + push af + and 3 + add a,'0' + printchar + pop af + bit 7,a + ret z + push af + ld a,'-' + printchar + pop af + rrca + rrca + and 3 + add a,'0' + printchar + ret + +;--- String data + +STR_INITIALIZING_NEXTOR: + db 13,"=== Initializing Nextor kernel at ",0 +STR_SLOT_SPACE: + db "slot ",0 +STR_SKIPPING_NEXTOR: + db 13,"-=- Skipping Nextor kernel at slot ",0 +STR_DRV_INIT_OK: + db 13,10,"--- Driver initialization OK",13,10,10,0 +STR_DRV_INIT_FAILED: + db 13,10,"*** Driver initialization failed!",13,10,10,0 +STR_UNNAMED_DRIVER: + db "(Unnamed driver)",0 +STR_3_DOTS: + db "...",0 +STR_NEXTOR_VERSION: + db "Nextor kernel version " + db MAIN_NEXTOR_VERSION##+"0", ".", SEC_NEXTOR_VERSION_HIGH##+"0" + db ".", SEC_NEXTOR_VERSION_LOW##+"0" + INCLUDE ../bank0/betainfo.mac + db 13,10 + db "github.com/konamiman/Nextor",13,10 + db 10 +STR_PRESS_N: + db "Press N to show the boot menu",13,10,10,0 + + finish + end diff --git a/source/kernel/bank4/jump.mac b/source/kernel/bank4/jump.mac index d7bf26ac..a891fea4 100644 --- a/source/kernel/bank4/jump.mac +++ b/source/kernel/bank4/jump.mac @@ -87,6 +87,9 @@ jentry macro name jentry F_RAMD jentry ZAP_RAMD + jentry BOOT_MENU + jentry PRINT_KERNEL_MSG + finish end diff --git a/source/kernel/bank4/partit.mac b/source/kernel/bank4/partit.mac index 84b7dd5b..b5ecc296 100644 --- a/source/kernel/bank4/partit.mac +++ b/source/kernel/bank4/partit.mac @@ -51,7 +51,7 @@ BUF equ 0F55Eh ;line input buffer (used as scratch) CHPUT: cp 10 jp nz,18h - rst 18h + printchar push hl ld hl,CKCNTC ld (BK4_ADD##),hl diff --git a/source/kernel/condasm.inc b/source/kernel/condasm.inc index 541d1474..2065f807 100644 --- a/source/kernel/condasm.inc +++ b/source/kernel/condasm.inc @@ -9,7 +9,6 @@ STURBO equ -1 ;support super turbo mode (for MSXturboR) BUILTIN equ -1 ;built-in system ALPHA equ 1 ;Level of Alpha release (0: no alpha) -ALPHAL equ "" ;Letter after the Alpha release number, or empty BETA equ 0 ;Level of Beta release (0: no beta) RC equ 0 ;Level of RC (0: no RC) RR equ 0 ;Level of release (0: no release level) diff --git a/source/kernel/data.mac b/source/kernel/data.mac index 2efa0604..dcdc7b4c 100644 --- a/source/kernel/data.mac +++ b/source/kernel/data.mac @@ -533,6 +533,9 @@ ram_ad defl 4100h var3 F_RAMD var3 ZAP_RAMD + var3 BOOT_MENU + var3 PRINT_KERNEL_MSG + ;----------------------------------------------------------------------------- ; ; Routines on ROM page 0 and on disk drivers diff --git a/source/kernel/drivers/StandaloneASCII8/driver.mac b/source/kernel/drivers/StandaloneASCII8/driver.mac index 2f4959d5..5e5c97d1 100644 --- a/source/kernel/drivers/StandaloneASCII8/driver.mac +++ b/source/kernel/drivers/StandaloneASCII8/driver.mac @@ -95,7 +95,8 @@ GWORK equ 4045h ;*** DRIVER CODE *** ;********************* - ;org 4100h + org 4000h + ds 256 DRIVER_START: @@ -222,8 +223,8 @@ DRIVER_QUERY: ; in B.C.D instead of A.B.C, and an error code is returned in A. DO_DRVQ_GET_VERSION: - ld bc,0102h - ld d,3 + ld bc,0100h + ld d,0 xor a ret @@ -332,7 +333,7 @@ JPIX: jp (ix) STR_DRIVER_NAME: db "Dummy standalone driver for Nextor",0 ; es bueno para la oreja, te lo pones asin y te crece la oreja",0 STR_DRIVER_AUTHOR: db "Konamiman",0 -INIT_MSG: db "Dummy standalone driver for Nextor\r\n" +INIT_MSG: db "\r\nDummy standalone driver for Nextor\r\n" db "by Konamiman\r\n",0 diff --git a/source/kernel/drv.mac b/source/kernel/drv.mac index 64a715c9..7d313ffb 100644 --- a/source/kernel/drv.mac +++ b/source/kernel/drv.mac @@ -981,9 +981,9 @@ DEFDPB: DW 7 ; - ALIGN 7BC0h + ALIGN 7E80h -; Hooks to jump to KABR (kernel disk error abort routine) and KDERR +; Hooks to jump to KABR (kernel disk error abort routine) and KDERR ; (kernel disk error handler routine) on disk error. ; ; When MSX-BASIC starts the KAB_VECT and KDSK_VECT vectors need to be set to @@ -1021,8 +1021,8 @@ KDERJ: nop jp KDERR## - ALIGN 7BD0h - ;Here goes the 1K free area at the end of banks 0 and 3 + ALIGN 7E90h + ;Here goes the free area at the end of banks 0 and 3 finish ; diff --git a/source/kernel/kvar.mac b/source/kernel/kvar.mac index 57837ba1..8e55aec4 100644 --- a/source/kernel/kvar.mac +++ b/source/kernel/kvar.mac @@ -291,6 +291,7 @@ size macro name ;it must be manually updated there if it changes. field 5,BOOTKEYS ;To store the state of boot keys field 1,I_AM_RUSSIAN + field 1,ALL_KERNELS_DISABLED ; ;----------------------------------------------------------------------------- ; diff --git a/source/kernel/macros.inc b/source/kernel/macros.inc index 755846f8..70855cbd 100644 --- a/source/kernel/macros.inc +++ b/source/kernel/macros.inc @@ -274,6 +274,10 @@ djpnz macro address jp nz,address endm +printchar macro + rst 18h + endm + ;------------------------------------------------------------------------------ ; .list From 4c77c2df3e7898817cae1164b77f0e74e2eec04e Mon Sep 17 00:00:00 2001 From: Nestor Soriano Date: Tue, 10 Feb 2026 22:51:54 +0100 Subject: [PATCH 2/4] Move KABJ/KDERJ disk error hooks from ROM to page 3 RAM The KABJ/KDERJ hooks act as intermediaries between the KBDOS error vectors and the KABR/KDERR handlers in bank 0, switching to bank 0 via CHGBNK so they work when bank 4 is paged in. Previously these hooks lived in ROM at matching addresses in banks 0 and 4 (dskab.mac), which forced the free area start from 7BD0h to 7E90h as the boot menu code grew. Moving the hooks to page 3 RAM eliminates the matching address requirement, restoring the full 1K free area at 7BD0h. --- source/kernel/Makefile | 3 +-- source/kernel/bank0/bdos.mac | 33 +++++++++++++++++--------- source/kernel/bank0/init.mac | 5 ++++ source/kernel/bank4/dskab.mac | 30 ------------------------ source/kernel/drv.mac | 44 ++--------------------------------- source/kernel/kvar.mac | 2 ++ 6 files changed, 32 insertions(+), 85 deletions(-) delete mode 100644 source/kernel/bank4/dskab.mac diff --git a/source/kernel/Makefile b/source/kernel/Makefile index 37974112..01cc1db0 100644 --- a/source/kernel/Makefile +++ b/source/kernel/Makefile @@ -353,7 +353,7 @@ B0RELS = $(addprefix bank0/,doshead.rel 40ff.rel b0.rel init.rel alloc.rel dskba B1RELS = $(addprefix bank1/,dosinit.rel mapinit.rel msg.rel) B2RELS = $(addprefix bank2/,kinit.rel char.rel dev.rel kbios.rel misc.rel seg.rel path.rel find.rel dir.rel handles.rel del.rel rw.rel files.rel buf.rel fat.rel val.rel err.rel) B3RELS = $(addprefix bank3/,dos1ker.rel) -B4RELS = $(addprefix bank4/,jump.rel env.rel cpm.rel ramdrv.rel time.rel seg.rel misc.rel dskab.rel bootmenu.rel) +B4RELS = $(addprefix bank4/,jump.rel env.rel cpm.rel ramdrv.rel time.rel seg.rel misc.rel bootmenu.rel) B5RELS = $(addprefix bank5/,b5.rel) B6RELS = $(addprefix bank6/,b6.rel) @@ -528,7 +528,6 @@ bank4/B4.BIN bank4/B4.SYM: \ $(call print_linking,"bank 4") @$(LK80) --working-dir bank4 --symbols-file B4.SYM --output-file B4.BIN \ --code 40FFh ../codes.rel ../kvar.rel ../data.rel b4.rel jump.rel env.rel cpm.rel partit.rel ramdrv.rel time.rel seg.rel misc.rel bootmenu.rel bootmsg.rel \ - --code 7E80h dskab.rel \ --code 7fd0h ../chgbnk.rel diff --git a/source/kernel/bank0/bdos.mac b/source/kernel/bank0/bdos.mac index 4eb299c4..0270eb74 100644 --- a/source/kernel/bank0/bdos.mac +++ b/source/kernel/bank0/bdos.mac @@ -34,6 +34,20 @@ DB_INIT:: ret ; ; +; Template for the KABJ/KDERJ hooks that get copied to page 3 RAM +; (KABJ_CODE and KDERJ_CODE) during initialization. These hooks switch +; to bank 0 via CHGBNK before jumping to KABR/KDERR, so they work +; regardless of which bank is currently paged in. +; +KABJ_HOOKS_TEMPLATE:: + xor a + call 7FD0h ;CHGBNK - switch to bank 0 + jp KABR + xor a + call 7FD0h ;CHGBNK - switch to bank 0 + jp KDERR +; +; ;----------------------------------------------------------------------------- ; KAB_ROUTINE: @@ -46,8 +60,9 @@ KABR:: ; the first BDOS call, switches back to the user's stack and jumps to the ; user's defined "BREAKVECT" routine. ; -; The hook at KAB_VECT is set to an intermediate hook KABJ instead of pointing -; directly here, see KABJ at the end of drv.mac for an explanation of why. +; The hook at KAB_VECT is set to an intermediate hook in page 3 RAM (KABJ_CODE) +; that switches to bank 0 via CHGBNK before jumping here. +; See KABJ_HOOKS_TEMPLATE above. ; ; exx ;Save error code in B' @@ -72,8 +87,9 @@ KDERR:: ; routine returns then the KBDOS paging is re-enabled and control returned to ; the KBDOS. ; -; The hook at KDSK_VECT is set to an intermediate hook KDERJ instead of pointing -; directly here, see KDERJ at the end of drv.mac for an explanation of why. +; The hook at KDSK_VECT is set to an intermediate hook in page 3 RAM (KDERJ_CODE) +; that switches to bank 0 via CHGBNK before jumping here. +; See KABJ_HOOKS_TEMPLATE above. ; ; Entry: A' = Old error code (passed to DISKVECT in C) ; A = New error code (passed to DISKVECT in A') @@ -186,14 +202,9 @@ DOS1_GO:: ; Insure error vectors are directed to us. Just for bad mannered applications ; that call 0005h at one time and F37Dh at another time. ...HF... ; -; Note: KABJ and KDERJ need to be defined at the same address as defined at drv.mac - -KABJ equ 7E80h -KDERJ equ KABJ+7 - - ld hl,KABJ ; ;Setup the disk vector and + ld hl,KABJ_CODE## ; ;Setup the disk vector and ld (KAB_VECT##),hl ; abort vector routines for - ld hl,KDERJ ; ; MSX-DOS 1.0 compatability. + ld hl,KDERJ_CODE## ; ; MSX-DOS 1.0 compatability. ld (KDSK_VECT##),hl ; LD HL,(BDOS_STACK##) OR A ;If the current stack pointer diff --git a/source/kernel/bank0/init.mac b/source/kernel/bank0/init.mac index 0e93a69a..193a7e0b 100644 --- a/source/kernel/bank0/init.mac +++ b/source/kernel/bank0/init.mac @@ -1434,6 +1434,11 @@ FILAUX: ld (hl),0C9h ld ($ERR_MSG##),hl ; error message routine. ld hl,PROMPT ;Record address of ld ($PROMPT##),hl ; disk change prompt routine. + ld hl,KABJ_HOOKS_TEMPLATE## ;Copy KABJ/KDERJ hook code + ld de,KABJ_CODE## ; to page 3 RAM so the hooks + ld bc,14 ; are ready before any BDOS + ldir ; call can happen. +; ld hl,BDOS_GO## ld ($BDOS##),hl ld hl,DOS1_GO## diff --git a/source/kernel/bank4/dskab.mac b/source/kernel/bank4/dskab.mac deleted file mode 100644 index e7a5624c..00000000 --- a/source/kernel/bank4/dskab.mac +++ /dev/null @@ -1,30 +0,0 @@ - .z80 - name('dskab') - -; Disk error handler and abort hooks for page 4. -; See KABJ and KDERJ at the end of bank0/drv.mac -; for an explanation of why this is needed. - - INCLUDE ../macros.inc - INCLUDE ../const.inc - - ;org 7FC0h - -; This is KABJ in bank 0 - xor a - call CHGBNK - ; Here is "jp KABR" in the bank 0 counterpart of the hook - nop - nop - nop - -; This is KDERJ in bank 0 - xor a - call CHGBNK - ; Here is "jp KDERJ" in the bank 0 counterpart of the hook - nop - nop - nop - - finish - end diff --git a/source/kernel/drv.mac b/source/kernel/drv.mac index 7d313ffb..63c7d45a 100644 --- a/source/kernel/drv.mac +++ b/source/kernel/drv.mac @@ -981,48 +981,8 @@ DEFDPB: DW 7 ; - ALIGN 7E80h - -; Hooks to jump to KABR (kernel disk error abort routine) and KDERR -; (kernel disk error handler routine) on disk error. -; -; When MSX-BASIC starts the KAB_VECT and KDSK_VECT vectors need to be set to -; KAB_ROUTINE and KDSK_ROUTINE, respectively, which are defined in bdos.mac. -; This is done at the start of BDOS_GO, also in bdos.mac. -; -; In MSX-DOS 2 and Nextor 2.0 the vectors are set to point directly to -; these routines. However in Nextor 2.1 we need an intermediate step since -; the old CP/M-style file handling functions have been moved to bank 4. Otherwise, -; if these functions throw an error, the routines are wrongly invoked while bank 4 -; is still switched (see GO_ABORT and GO_EXIT in bank2/err.mac), -; causing the computer to crash since the routines are actually in bank 0. -; -; Thus, the vectors are set to point to these intermediate KABJ and KDERJ hooks. -; These exist also in bank 4 (see banck/dskab.mac), where they have -; "xor a - call CHGBNK" at the same address of the four "nop"s here. Thus right after -; bank 0 is restored, the jp KABR/KDERR is executed and the error handler -; continues as expected. -; -; KABR and KDERR are also defined in bank3/dos1ker.mac in order to prevent a -; compilation error. The definitions there are stubs since these hooks are -; never used in MSX-DOS 1 mode. - -KABJ: - nop - nop - nop - nop - jp KABR## - -KDERJ: - nop - nop - nop - nop - jp KDERR## - - ALIGN 7E90h - ;Here goes the free area at the end of banks 0 and 3 + ALIGN 7BD0h + ;Here goes the 1K free area at the end of banks 0 and 3 finish ; diff --git a/source/kernel/kvar.mac b/source/kernel/kvar.mac index 8e55aec4..685e8b50 100644 --- a/source/kernel/kvar.mac +++ b/source/kernel/kvar.mac @@ -292,6 +292,8 @@ size macro name field 5,BOOTKEYS ;To store the state of boot keys field 1,I_AM_RUSSIAN field 1,ALL_KERNELS_DISABLED + field 7,KABJ_CODE ;RAM hook: xor a + call CHGBNK + jp KABR + field 7,KDERJ_CODE ;RAM hook: xor a + call CHGBNK + jp KDERR ; ;----------------------------------------------------------------------------- ; From 74e3b88fc7f7204ab69fc942b30575523e936442 Mon Sep 17 00:00:00 2001 From: Nestor Soriano Date: Wed, 11 Feb 2026 12:31:18 +0100 Subject: [PATCH 3/4] Add the AI conversation --- .ai/CONVERSATION.md | 238 ++++++++++++++++++++++++++++++++++++++++++++ .ai/PLAN.md | 84 ++++++++++++++++ .ai/SESSION.md | 162 ++++++++++++++++++++++++++++++ 3 files changed, 484 insertions(+) create mode 100644 .ai/CONVERSATION.md create mode 100644 .ai/PLAN.md create mode 100644 .ai/SESSION.md diff --git a/.ai/CONVERSATION.md b/.ai/CONVERSATION.md new file mode 100644 index 00000000..9881a79f --- /dev/null +++ b/.ai/CONVERSATION.md @@ -0,0 +1,238 @@ +# Nextor Boot Menu Design Review - Full Conversation Archive + +## Date: 2026-02-08 + +--- + +## User + +Read .ai/PLAN.md for instructions. + +--- + +## Claude + +[Read /home/konamiman/Nextor/.ai/PLAN.md] + +[Launched three parallel research agents to explore: boot process code, driver query protocol, and boot keys documentation] + +[After research completed, also read doshead.mac and init.mac sections for specific details] + +Now I have a thorough understanding of the codebase. Here's my analysis of the boot menu design: + +--- + +## Design Validation + +The overall design is solid and well-thought-out. The core idea of leveraging the existing one-time boot keys mechanism (RAM keys at A100h) as the output mechanism for the menu is, in my view, the key to making this work cleanly. Here are my detailed observations: + +### Things that work well in the design + +1. **Repurposing the N key** from "disable all" to "show menu" is a natural evolution. The menu provides equivalent functionality (and more) via the "N. Disable all and boot" option inside the menu. + +2. **The 5-kernel display limit** is practical. With 5 kernel lines + 8 numeric lines + header/footer, the menu fits in 24-line text mode (about 21 lines total). + +3. **Using per-slot disable keys (UIOP/QWER/ASDF/ZXCV) as the toggle keys in the menu** preserves backward compatibility for experienced users and avoids inventing a new key assignment scheme. + +### Important issues and suggestions + +#### 1. Use the one-time boot keys mechanism to prevent double menu display + +The plan mentions needing an "in-memory flag" to prevent a second kernel from showing the menu. I'd suggest that the one-time boot keys mechanism at A100h is sufficient by itself, with no extra flag needed. Here's why: + +- The menu **always** writes RAM keys (for all three exit paths: ENTER, N, and ESC), with the **N bit cleared**. +- When the next kernel calls `DO_KEYS_INIT`, `SCANKEYS_RAM` finds the RAM keys and loads them instead of scanning the keyboard. +- Since the N bit is cleared in the RAM keys, the next kernel's "show menu?" check fails. +- For **ESC** specifically: write RAM keys = current BOOTKEYS with N bit cleared. This preserves all other key states while preventing menu re-display. +- For **ENTER**: write RAM keys = user's toggled selections with N bit cleared. +- For **N (disable all)**: write RAM keys = all slot disable keys set, N bit cleared. + +This is fully self-consistent and requires no separate flag. + +#### 2. Separate the N check from DISABLE_KEY + +Currently `DISABLE_KEY` checks N first (line 226-229) and returns NZ (disabled) if N is pressed. The new design should split this: + +- `DISABLE_KEY` checks **only per-slot keys** (remove the N check) +- A separate check for N triggers the menu at the right point in the init flow + +The revised init flow for the master kernel would be: +``` +CHK_DUP → DO_KEYS_INIT → DISABLE_KEY (per-slot only) → CHK_DRIVER → +DISKID check → [if master AND N pressed → SHOW_MENU → re-run DISABLE_KEY] → +continue normal init +``` + +The re-run of `DISABLE_KEY` after the menu is important: if the user disabled the current kernel in the menu, the updated BOOTKEYS will have its per-slot key set, and DISABLE_KEY will return NZ. + +#### 3. Don't show the menu when using programmatic RAM keys + +If the NEXTOR_BOOT_KEYS signature already exists at A100h when the kernel boots (someone used NEXBOOT.COM), the menu should **not** be shown, even if those RAM keys have the N bit set. RAM keys are meant for automated/programmatic boot control, and showing an interactive menu would defeat the purpose. + +This is already handled naturally: `DO_KEYS_INIT` calls `SCANKEYS_RAM` first, which loads from RAM and skips keyboard scanning. Then KEYS_INV is not applied. But you should explicitly verify that programmatic N-in-RAM-keys doesn't trigger the menu. The check could be: "show menu if N is in BOOTKEYS AND keys came from keyboard (not from pre-existing RAM keys)." A simple flag set by `SCANKEYS_RAM` when it finds pre-existing keys would work. + +#### 4. NEXTOR3 signature placement: use 400Ah-400Fh + +The MSX ROM header has 6 reserved bytes at 400Ah-400Fh (the last three `defw 0` in doshead.mac at lines 25-27). These are safe to use. A short signature like `"NXT3"` (4 bytes) at 400Ah would be sufficient when combined with the "AB" check at 4000h. The remaining 2 bytes could hold a flags/version byte. + +The scanning algorithm for each slot would be: +1. `RDSLT` at 4000h/4001h → check for "AB" (safe, read-only) +2. `RDSLT` at 400Ah-400Dh → check for "NXT3" (safe, read-only) +3. If both match: `CALSLT` + `CALDRV` at 4048h → `DRIVER_QUERY` with A=2, B=1 to get the driver name string + +Step 3 is safe because CALDRV/CALBNK in the target ROM are in doshead.mac (ROM code, no initialization needed), and the bank switching code at CHGBNK is also ROM code. BK4_ADD is safe to use at this boot stage since no timer interrupt hooks have been set up yet. + +Important: **don't use the full 8 bytes** including 4008h-4009h. That word is the "BASIC program text" pointer in the MSX ROM header, and some BIOSes or tools might check it. The 6 bytes at 400Ah-400Fh are truly unreserved. + +#### 5. The driver name query without initialization + +This requirement is well-identified in the plan. Looking at the existing drivers (StandaloneASCII8, SunriseIDE, MegaFlashRomSD), `DRVQ_GET_STRING` just copies a constant string - it has no dependency on `DRVQ_INIT`. This should be fine for well-behaved drivers. + +However, the `DRVQ_GET_STRING` implementation uses `OUTPUT_STRING` which takes `HL`=source, `DE`=destination buffer, `D`=buffer size. The buffer address is in the master kernel's RAM space. Since `CALSLT` doesn't change the RAM page mapping (page 3 is always RAM), the target driver can write to the buffer address provided by the master. This should work. + +One subtle point: the buffer size `D` should account for the "..." suffix. If the driver returns `QUERY_TRUNCATED_STRING`, the menu needs 3 extra characters for "...". So pass `D = available_width - 3` when querying, and append "..." if truncated. + +#### 6. Screen state considerations + +At the point where the menu would be shown, the screen is in whatever state the BIOS left it (usually 40-column text mode after power-on). The code needs to: + +- Detect MSX version (check MSXVER at 002Dh or the version byte at FCC1h) for 40/80 column decision +- Call `INITXT` (006Ch) or set screen mode via BIOS to ensure text mode +- For 80 columns on MSX2+: set `LINL40` (F3AEh) to 80 before calling INITXT, or use the BIOS `WIDTH` routine + +After the menu is dismissed, the normal init flow's `OVERRIDE` code sets up the screen properly, so no special cleanup is needed. + +#### 7. What about mixed Nextor 2 and Nextor 3 scenarios? + +Nextor 2 kernels won't have the "NXT3" signature, so they won't appear in the menu's kernel list. They'll be cleaned up by `CLEAN_V2_KERNELS` regardless. This seems correct - the boot menu is a Nextor 3 feature and shouldn't try to manage Nextor 2 kernels. + +MSX-DOS kernels (non-Nextor) also won't appear. The "7. Disable MSX-DOS kernels (SHIFT)" option in the menu provides the mechanism for controlling those. + +### Questions + +1. **Key 0 (Disable permanent disk emulation)**: Should this option always appear in the menu, or only when disk emulation is actually active? Showing it always is simpler and serves as a memory aid, but might confuse users who don't use disk emulation. + +2. **5th kernel display**: If 5 Nextor 3 kernels are detected, the 5th can't be registered (KERNEX has 4 slots). Should the menu indicate this limitation? Or should the 5th kernel simply be shown like the others, with the understanding that the existing KERNEX overflow behavior applies? + +3. **Key 3 (boot to BASIC) default state**: With `KEYS_INV_0 = 00h` (default), key 3 is not inverted, meaning the asterisk appears only when the user toggles it in the menu. Is the intended behavior that key 3 is **off** by default (normal boot) and the user toggles it **on** to force BASIC? That's what the design implies and it seems correct, just want to confirm. + +4. **What if the master kernel's own driver signature check (`CHK_DRIVER`) fails?** Currently the system halts. With the new flow, should the menu be shown before `CHK_DRIVER`? That would allow the user to disable a broken kernel, but it would also mean showing the menu from a kernel that can't validate its own driver. I'd suggest keeping `CHK_DRIVER` before the menu check - a kernel with a bad driver shouldn't be showing a menu. + +5. **Timing of the menu relative to the "beep"**: Currently the first kernel plays a beep during init (line ~897). Should the menu appear before or after the beep? Before seems better (immediate visual feedback), and the beep could happen after the menu is dismissed. + +--- + +## User + +First, about your questions: + +1. The "permanent disk emulation mode" is the one that's reigstered by writing data in the partition table of the first device available in the main kernel (see the second table in https://github.com/Konamiman/Nextor/blob/v2.1/docs/Nextor%202.1%20Programmers%20Reference.md#722-entering-disk-emulation-mode). We would need to read that to verify if the data exists, but we can't access any device before the kernels are initialized, therefore we must always show this menu entry. +2. Five kernels will be a very rare case, so let's keep thing simple and just show them with the understanding that no all of them will be initialized. +3. Yes, as it's the case with all the other keys: initially show the asterisk only if the key is inverted (so considered pressed by default) in KEYS_INV_0/1. +4. Good point. But also, what if any of the other drivers has a bad driver too? It won't be possible to get its name. In this case the name shown should be "(BAD DRIVER)" and it shouldn't be possible to mark it as active. +5. Beep generally means "all good, let's boot" so yes, after the menu makes sense. + +Now about other things you mentioned: + +- Signature placement: I don't like the idea of using 400Ah-400Fh because the MSX standard explicitly says that this should be zero. It's very likely safe to use it anyway, but sill, I prefer to keep things clean. There's actually one 5 byte gap in that code at 407B, that's enough for "NXT3" with a zero terminator so let's do that (I actually placed the signature there already). +- Screen state considerations: the screen is already being initialized before passing control to the initialization routine of the first driver, so maybe we just need to move that part back in the boot procedure. By the way look at PrintRuler() in fdisk.c for an important gotcha. + +--- + +## Claude + +[Read doshead.mac lines 119-133 - confirmed NXT3_SIGNATURE already at 407Bh] +[Read init.mac lines 870-999 - reviewed the first cartridge / OVERRIDE flow] +[Searched for PrintRuler in fdisk.c - found the Korean MSX gotcha] +[Read init.mac lines 1000-1089 - reviewed VER_CHK and OVERRIDE code] +[Read init.mac lines 3540-3564 - reviewed CALL_DRIVER_QUERY] +[Searched for H_CHPH references - confirmed address FDA4h] + +Good, the Korean MSX gotcha in PrintRuler and the existing signature placement are clear. Let me also check the OVERRIDE screen init flow and how `DRVQ_GET_STRING` would be called on other slots. + +Now I have a complete picture. Here's my updated analysis incorporating your feedback: + +--- + +## Updated Design Analysis + +### Signature: confirmed at 407Bh + +The `NXT3_SIGNATURE` at 407Bh fits perfectly in the 5-byte gap between `jp BIOS_IN##` (4078h-407Ah) and RAMENT (4080h). Clean and doesn't touch any reserved header bytes. + +### Korean MSX gotcha + +The `PrintRuler` in fdisk.c checks if H_CHPH (FDA4h) is hooked (not 0xC9). Korean MSX computers do weird things when printing a character at the last screen column. The boot menu's separator lines need the same check: print `width - 1` hyphens when H_CHPH is hooked. + +### Screen initialization: move INITXT earlier + +The INITXT call at `OVERRIDE` (line 1032-1039) already sets 40/80 columns based on MSXVER. This naturally becomes the screen setup for the menu. The menu check would be inserted right after INITXT, before the version/hook setup: + +``` +OVERRIDE: + [set LINL40, call INITXT] ← existing code + [check N key + show menu] ← NEW + [set DOS_VER, NXT_VER, hooks] ← existing code continues +``` + +The CLS at line 893 (first cartridge path) becomes redundant since INITXT clears the screen, but it's harmless. + +### "(BAD DRIVER)" handling + +For the scanning kernel itself: `CHK_DRIVER` runs before the menu and halts on failure, so the scanning kernel always has a valid driver. + +For other kernels found during scanning: after confirming "NXT3" at 407Bh via RDSLT (safe), the menu code would try `DRVQ_GET_STRING` via CALSLT+CALDRV. The existing `PRINT_INIT_KERNEL_AT_ASLOT` at line 695 already handles three outcomes: +- `QUERY_OK` (A=0): display the name from buffer +- `QUERY_TRUNCATED_STRING` (A=1): display name + "..." +- Anything else: display fallback string + +For the menu, the fallback would be "(BAD DRIVER)" instead of "(unnamed driver)". And crucially: a kernel shown as "(BAD DRIVER)" should be **locked as disabled** (no asterisk, toggling ignored). This prevents users from trying to boot with a broken driver. + +One risk: if the ROM at the other slot has "NXT3" at 407Bh but corrupt bank-switching code at CHGBNK, the inter-slot call through CALDRV could crash rather than return an error. The NXT3 signature gives high confidence this won't happen (it implies a properly built Nextor 3 kernel), but a truly corrupted ROM is always possible. I think this is an acceptable risk - a ROM corrupt enough to have valid "NXT3" but broken CHGBNK would likely crash during normal boot anyway. + +### Remaining design points I want to confirm + +1. **One-time boot keys as the sole anti-re-display mechanism**: After the menu is shown, RAM keys are always written (all three exit paths), with N bit cleared. Subsequent kernels' `SCANKEYS_RAM` picks these up, never sees N, never triggers the menu. No extra flag needed. Does this match your intent? + +2. **Programmatic RAM keys (NEXBOOT.COM) with N bit**: If someone writes RAM keys with N set via NEXBOOT.COM, the current design would NOT show the menu (since `SCANKEYS_RAM` returns Z and the keyboard is skipped). N-in-RAM-keys would need to be handled as "disable all kernels" the old way. Do you want to preserve this behavior, or should programmatic N-in-RAM-keys also trigger the menu? + +3. **The disabled-via-commented-out code at lines 1046-1063**: This was an attempt to kill Nextor v2 kernels by simulating an N key press via RAM keys. It's disabled (`if 0`) because it also kills Nextor v3 kernels. With the boot menu, this code becomes unnecessary since `CLEAN_V2_KERNELS` handles v2 cleanup. Should this dead code be removed as part of the boot menu implementation? + +4. **Menu insertion point for the "override" case (line 1001-1026)**: When a newer kernel overrides an older one, it also reaches OVERRIDE. At that point, a PREVIOUS Nextor 3 kernel may have already shown the menu and written RAM keys. So the overriding kernel's `DO_KEYS_INIT` would have loaded the RAM keys (no N). The menu wouldn't trigger. This is correct behavior - the previous master already showed the menu. Agreed? + +--- + +## User + +1 and 2: if the user uses NEXBOOT.COM or a similar tool to explicitly set the boot keys and reboot, it's naturally assumed that he exactly knows how the new boot must look like so the menu isn't needed. +3. This is leftover code that can go away. +4. Yes, agreed. + +However, don't write any code now. Write the entire conversation for this session (including this) in .ai/SESSION.md and we'll continue at another time. + +--- + +## Claude + +[Wrote .ai/SESSION.md with a structured summary of all design decisions, technical details, and open items] + +Done. The full session is saved in `.ai/SESSION.md` covering: + +- All design decisions made (signature at 407Bh, one-time boot keys as anti-re-display, DISABLE_KEY split, init flow, screen init, Korean MSX gotcha, BAD DRIVER handling) +- Your answers to my questions (programmatic RAM keys skip menu, dead code removal, override case) +- Technical reference details (BOOTKEYS layout, RAM keys, inter-slot calls, key mappings, code locations) +- The scanning algorithm and screen layout analysis + +Ready to pick up implementation from here in a future session. + +--- + +## User + +That document is fine for continuing the work. But for archival purposes, I want a verbatim copy of the entire converstaion, including my prompts and you answers. Write that at .ai/ARCHIVE.md + +--- + +## Claude + +[Wrote this file] diff --git a/.ai/PLAN.md b/.ai/PLAN.md new file mode 100644 index 00000000..e59c834b --- /dev/null +++ b/.ai/PLAN.md @@ -0,0 +1,84 @@ +# Nextor boot menu specification + +## The problem + +There are several keys that a Nextor user can press while bootin in order to modify the boot behavior: slot keys to prevent specific Nextor kernels from booting (a key per slot number); and numeric keys for behavior like e.g. booting in DOS 1 mode or directly to BASIC. + +This is confusing as the user needs to remember several keys and do some "gymnastics" if he wants to press several keys at once. So this is a proposal for a more user-friendly equivalent mechanism. + +## The Nextor boot menu + +The idea is that rigth when a Nextor 3 kernel boot sequence starts, and as soon as it detects that it is the first kernel in the system (or it's taking over a Nextor 2 or a MSX-DOS kernel) and if the user is pressing the N key, it displays a boot menu similar to this: + +``` + Nextor 3.0.0 boot menu +---------------------------------------- + +* R. MegaFlashROM SCC+ SD in slot 1-3 +* S. Kernel with a vey lo... in slot 2-1 +* Q. ... +* W. (no name) in slot 1-0 +* E. ... + N. Disable all and boot + + 0. Disable permanent disk emulation + 1. Boot in MSX-DOS 1 mode + 2. Boot in MSX-DOS 1+R800 ROM mode + 3. Boot in MSX-BASIC + 4. Boot in R800 ROM mode + 5. Reduced drive allocation mode +* 6. Single FDD drive (CTRL) + 7. Disable MSX-DOS kernels (SHIFT) + +--------------------------------------- + ESC = Cancel, ENTER = Apply and boot +``` + +where the first half consists of all the Nextor 3 kernels present in the system, enabled (prepended by "*") by default; and the second half is the state of the numeric boot keys, by default those that have their bit set to 1 in KEYS_INV_0/1 in init.mac. + +Thus the high level sequence of events would be: + +1. The Nextor kernel detecting that it's the (new) master and that the N key is being pressed initializes the screen in text mode (40 columns in MSX1, 80 columns otherwise). +2. The Nextor kernels are detected by scanning all the existing slots (more on that later) and the name of each is requested via the device query routine. +3. All the kernel names and slots are displayed, together with their assigned slot disable key, and with a "*" indicating that in principle the kernel will boot normally. +4. All the numeric boot modification keys are shown (except 2 and 4 in non-Turbo-R computers). An asterisk is shown for the keys that are inverted according to KEYS_INV_0/1 in init.mac. +5. The kernel waits for the user to release the N key and waits for other key pressings. +6. If one of the keys shown is pressed (slot key or numeric key) the corresponded entry is toggled. +7. If the user presses ESC, the menu disappears and the boot proceeds normally, discarding any changes. It's as if the menu hadn't been shown. +8. If the user presses ENTER, the enabled entries are collected and the BOOTKEYS variable is set appropriately, including the NEXTOR_BOOT_KEYS signature. Then the menu disappears and the boot procedure continues. +9. If the user presses N, it's as when pressing ENTER, except that ALL the Nextor kernels are disabled. Thus an easy(ish) wait to disable all Nextor kernels is: press N, wait for the menu, release, press again. +10. The registered keys influence the boot procedure of all kernels as if the keys had been physically pressed or if the one-time boot keys mechanism had been used. + +This is practical for one-off unusual boots, and also as a memory aid for users to remember which key does what without having to look for documentation. + +## Technical details, nuances and challenges + +- The menu needs to be shown before any Nextor driver has been initialized. Therefore all the existing slots need to be examined at that point in search for Nextor 3 kernels (there's no KERNEX table built yet). This implies that we need to put a short signature (like "NEXTOR3") at a reachable location in all the ROM pages of the Nextor kernel. The first 256 bytes page (dosehad.mac) seems like a good place, but that area is already quite cramped so some optimization work might be needed first. + +- Theorically a MSX computer can have up to 16 slots (if all four main slots are extended), but the detection procedure can stop as soon as five kernels are detected: it's extremely unlikely that any real system will have more Nextor kernels, and only four are supported anyway given the current architecture of Nextor. Also this allows the full kernels list and the numeric keys list to fit in the screen. + +- An interesting case is that the main kernel (the one showing the menu) can be itself disabled via the boot menu. But then there can be another Nextor kernel located in a higher slot, which hasn't been disabled. This one will become the effective master but it must NOT show the menu again, so some in-memory flag is probably needed for that. + +- Drivers must support the "Get driver information string" driver query (and the "Get driver version number" query, for possible future enhancements of the boot menu) WITHOUT the "Initialize driver" query having been executed first. This needs to be very clearly documented. + +- The boot menu layout needs to support 40 columns and 80 columns for MSX1 and MSX2/2+/Turbo-R. This implies adjustments on how the top and bottom lines are centered, the length of the hyphens lines, and the maximum string length provided to the driver when querying its name (and if the driver says that the name is truncated, "..." should be shown after it). + +- Related to the above: in 40 columns mode the "in slot X[-Y]" text could be shortened to "in X[-Y]" so we can display five more characters of the driver name. + +- Another interesting case: Nextor 3.0 boots and shows the menu, then later on a hypothetical Nextor 3.1 boots and becomes the new master. In this case too, care must be taken so the menu is shown only once. Also: Nextor 3.1 might have a newer version of the code that shows the menu, but it's the 3.0 kernel the one that needs to show it, because otherwise it wouldn't be possible for it to disable itself (not ideal but acceptable). + +- For extra coolness we could temporarily redefine the asterisk character like a checkmark, but let's worry about that once a working implementation of the menu is in place. + +- This needs to be compatible with the mechanism to disable Nextor 2 kernels that was implemented in https://github.com/Konamiman/Nextor/pull/193 . + +## Additional information + +- source/kernel/drivers/StandaloneASCII8/driver.mac for the structure of the Nextor 3 drivers. +- https://github.com/Konamiman/Nextor/blob/v2.1/docs/Nextor%202.1%20User%20Manual.md for the one-time keys configuration tool and the boot keys reference. +- https://github.com/Konamiman/Nextor/blob/v2.1/docs/Nextor%202.1%20Programmers%20Reference.md for the technical details on how the one-time boot keys works (section 7). +- https://github.com/Konamiman/MSX2-Technical-Handbook/blob/master/md/Appendix1.md and https://github.com/Konamiman/MSX2-Technical-Handbook/blob/master/md/Appendix4.md for the MSX BIOS and work area variables and buffers. + +## Task for you + +Validate the idea and the design. Is there something important I haven't taken in account? Can you suggest improvements or simplifications? Do you have questions? + diff --git a/.ai/SESSION.md b/.ai/SESSION.md new file mode 100644 index 00000000..b5286ba0 --- /dev/null +++ b/.ai/SESSION.md @@ -0,0 +1,162 @@ +# Nextor Boot Menu - Design Review Session + +## Date: 2026-02-08 + +## Context + +Review and validation of the boot menu design described in `.ai/PLAN.md`. No code was written - this was a pure design discussion session. + +## The Design (Summary from PLAN.md) + +When a Nextor 3 kernel boots and detects it's the master, if the user is pressing the N key, it displays an interactive boot menu showing: +- All detected Nextor 3 kernels (with names from driver query) and their toggle keys +- Numeric boot key options (0-5 plus CTRL=6, SHIFT=7) +- ESC to cancel, ENTER to apply, N to disable all kernels and boot + +## Key Design Decisions Made + +### Signature placement +- "NXT3\0" signature already placed at 407Bh in doshead.mac (5-byte gap between `jp BIOS_IN##` at 4078h and RAMENT at 4080h) +- Avoids using 400Ah-400Fh reserved bytes (MSX standard says those should be zero) +- Slot scanning uses RDSLT to check for "NXT3" at 407Bh - safe, read-only operation + +### One-time boot keys as the anti-re-display mechanism +- After the menu is shown, RAM keys (at A100h) are ALWAYS written for all three exit paths (ENTER, N, ESC), with the N bit cleared +- Subsequent kernels' `SCANKEYS_RAM` picks these up, never sees N, never triggers the menu +- No separate "menu already shown" flag is needed - the mechanism is self-consistent +- For ESC: write RAM keys = current BOOTKEYS with N bit cleared (preserves all other key states) +- For ENTER: write RAM keys = user's toggled selections with N bit cleared +- For N (disable all): write RAM keys = all slot disable keys set, N bit cleared + +### Programmatic RAM keys (NEXBOOT.COM) +- If RAM keys already exist at A100h when booting (set by NEXBOOT.COM or similar), the menu is NEVER shown, even if those RAM keys have N set +- Rationale: programmatic boot keys imply the user knows exactly what boot configuration they want +- N-in-RAM-keys continues to mean "disable all kernels" the old way + +### DISABLE_KEY modification +- Current `DISABLE_KEY` checks N first (disables all kernels) then per-slot keys +- New design: split the N check out of `DISABLE_KEY`, so DISABLE_KEY only checks per-slot keys +- N key triggers the menu instead of directly disabling +- After menu updates BOOTKEYS, DISABLE_KEY is re-run to check if the current kernel was disabled by the user's selections + +### Init flow for the menu +``` +CHK_DUP → DO_KEYS_INIT → DISABLE_KEY (per-slot only) → CHK_DRIVER → +DISKID check → [if master: reach OVERRIDE] → +OVERRIDE: set LINL40, call INITXT → +[if N pressed AND keys came from keyboard (not RAM): SHOW_MENU, update BOOTKEYS, write RAM keys] → +[re-run DISABLE_KEY with updated BOOTKEYS] → +continue normal OVERRIDE (set DOS_VER, NXT_VER, H.RUNC hook) → SLAVE (driver init) +``` + +The menu is shown AFTER INITXT (screen is ready) and AFTER CHK_DRIVER (own driver validated), but BEFORE driver initialization. + +### Screen initialization +- The existing INITXT call at OVERRIDE (line 1032-1039) already sets 40/80 columns based on MSXVER +- The menu check is inserted right after INITXT, before version/hook setup +- The CLS at line 893 (first cartridge path) becomes redundant but harmless + +### Korean MSX gotcha +- From `PrintRuler()` in fdisk.c: Korean MSX computers do weird things when printing at the last screen column +- Check if H_CHPH (FDA4h) != 0xC9 (hooked); if so, print separator lines with `width - 1` characters +- The boot menu's separator/ruler lines must implement this same check + +### "(BAD DRIVER)" handling +- For the scanning kernel itself: CHK_DRIVER runs before the menu and halts on failure, so the scanning kernel always has a valid driver +- For other kernels found during scanning: + 1. RDSLT to check "NXT3" at 407Bh (safe) + 2. Try DRVQ_GET_STRING via CALSLT+CALDRV to get driver name + 3. If query returns QUERY_OK or QUERY_TRUNCATED_STRING: display the name + 4. If query returns anything else: display "(BAD DRIVER)" +- A kernel shown as "(BAD DRIVER)" is locked as disabled (no asterisk, toggle ignored) +- Risk of crash from corrupt ROM accepted as unlikely (NXT3 signature implies properly built kernel) + +### Dead code cleanup +- The commented-out code at init.mac lines 1046-1063 (simulating N key via RAM keys to kill Nextor v2 kernels) is leftover and should be removed as part of this implementation + +### Override case (newer kernel becomes master) +- If kernel A (3.0) shows the menu and writes RAM keys, then kernel B (3.1) overrides A and becomes master +- Kernel B's DO_KEYS_INIT loads RAM keys (no N set) → menu doesn't trigger again +- This is correct: the previous master already showed the menu + +## Scanning Algorithm + +For each slot/subslot combination (up to 16, but stop at 5 kernels found): +1. Check EXPTBL (FCC1h) to determine if primary slot is expanded +2. RDSLT at 4000h/4001h → check for "AB" ROM signature +3. RDSLT at 407Bh-407Eh → check for "NXT3" signature +4. If both match: set BK4_ADD = DRIVER.DRIVER_QUERY (412Bh), CALSLT with IX=CALDRV (4048h), IYh=target slot, A=DRVQ_GET_STRING, B=DRVQ_STR_DRIVER_NAME, D=buffer size, HL=buffer address +5. Handle result: QUERY_OK → name, QUERY_TRUNCATED_STRING → name+"...", other → "(BAD DRIVER)" + +Buffer size for driver name query should be `available_width - 3` to leave room for "..." suffix when truncated. + +## Screen Layout Analysis + +Total lines with 5 kernels + 8 numeric options: ~21 lines. Fits in 24-line MSX text mode. + +### 40-column mode (MSX1) +- Shorten "in slot X-Y" to "in X-Y" for 5 extra characters of driver name +- Separator: 40 (or 39 for Korean MSX) hyphens +- Max driver name display width: roughly 25-28 characters + +### 80-column mode (MSX2+) +- Full "in slot X-Y" text +- Separator: 80 (or 79 for Korean MSX) hyphens +- Max driver name display width: roughly 55-60 characters + +### Non-Turbo-R +- Keys 2 and 4 (R800-related) are hidden, saving 2 lines + +## Key Mappings in the Menu + +Kernel toggle keys use the existing per-slot disable keys from DISABLE_TBL: +- Slot 0-0 to 0-3: U, I, O, P +- Slot 1-0 to 1-3: Q, W, E, R +- Slot 2-0 to 2-3: A, S, D, F +- Slot 3-0 to 3-3: Z, X, C, V + +Numeric keys: 0-5 as labeled, plus 6=CTRL, 7=SHIFT + +Special: N = disable all and boot, ESC = cancel, ENTER = apply and boot + +## Default States (Asterisks) + +- All kernels: enabled (asterisk shown) by default +- Numeric keys: asterisk shown if the corresponding bit is set in KEYS_INV_0/KEYS_INV_1 +- Default KEYS_INV_0 = 00h → no numeric keys inverted +- Default KEYS_INV_1 = 20h → CTRL inverted (key 6 "Single FDD drive" shows asterisk by default) + +## Technical Notes for Implementation + +### BOOTKEYS layout (5 bytes at F9B9h) +- Byte 0 (B): 76543210 (digit keys) +- Byte 1 (E): FEDCBA98 +- Byte 2 (D): NMLKJIHG +- Byte 3 (L): VUTSRQPO +- Byte 4 (H): CAPS.GRAPH.CTRL.SHIFT.ZYXW + +### RAM keys at A100h +- 17 bytes: "NEXTOR_BOOT_KEYS\0" +- 5 bytes: key data (same layout as BOOTKEYS: B, E, D, L, H) +- RAM keys bypass KEYS_INV (inversion already applied or not needed) + +### Inter-slot call safety +- BK4_ADD is safe to use at this boot stage (no timer interrupt hooks set up yet) +- CALDRV/CALBNK in target ROM are doshead.mac code (ROM, no initialization needed) +- Bank switching code at CHGBNK is ROM code, works without initialization + +### Existing code references +- `PRINT_INIT_KERNEL_AT_ASLOT` at init.mac:695 - existing driver name query logic (local slot only) +- `CALL_DRIVER_QUERY` at init.mac:3556 - calls DRIVER_QUERY in local slot via CALBNK +- `DISABLE_KEY` at init.mac:226 - current N + per-slot key check (to be split) +- `DO_KEYS_INIT` at init.mac:3024 - keyboard/RAM key scanning +- `SCANKEYS_RAM` at init.mac:634 - one-time RAM keys detection +- `OVERRIDE` at init.mac:1028 - master kernel setup (menu insertion point) +- `CLEAN_V2_KERNELS` at init.mac:3574 - Nextor 2 cleanup (separate from menu) + +## Open Items / Nice-to-haves + +- Redefine asterisk character pattern as a checkmark (VDP pattern table modification) - defer until working implementation exists +- Consider what happens with exactly 5 kernels (KERNEX only has 4 slots) - show all 5 in menu, accept that the 5th can't register +- Key 0 "Disable permanent disk emulation" always shown (can't check device before driver init) +- Driver query without initialization requirement must be documented for third-party driver developers From 57f2a927c78da1f5534982145a1f94a4bc0e3412 Mon Sep 17 00:00:00 2001 From: Nestor Soriano Date: Wed, 11 Feb 2026 13:17:25 +0100 Subject: [PATCH 4/4] Small fixes --- .ai/PLAN.md | 4 ++-- source/kernel/Makefile | 2 +- source/kernel/bank0/init.mac | 7 ++++++- source/kernel/bank4/bootmenu.mac | 2 -- source/kernel/bank4/bootmsg.mac | 12 ++++-------- source/kernel/drivers/StandaloneASCII8/driver.mac | 3 +-- 6 files changed, 14 insertions(+), 16 deletions(-) diff --git a/.ai/PLAN.md b/.ai/PLAN.md index e59c834b..9eaa2eb2 100644 --- a/.ai/PLAN.md +++ b/.ai/PLAN.md @@ -2,13 +2,13 @@ ## The problem -There are several keys that a Nextor user can press while bootin in order to modify the boot behavior: slot keys to prevent specific Nextor kernels from booting (a key per slot number); and numeric keys for behavior like e.g. booting in DOS 1 mode or directly to BASIC. +There are several keys that a Nextor user can press while booting in order to modify the boot behavior: slot keys to prevent specific Nextor kernels from booting (a key per slot number); and numeric keys for behavior like e.g. booting in DOS 1 mode or directly to BASIC. This is confusing as the user needs to remember several keys and do some "gymnastics" if he wants to press several keys at once. So this is a proposal for a more user-friendly equivalent mechanism. ## The Nextor boot menu -The idea is that rigth when a Nextor 3 kernel boot sequence starts, and as soon as it detects that it is the first kernel in the system (or it's taking over a Nextor 2 or a MSX-DOS kernel) and if the user is pressing the N key, it displays a boot menu similar to this: +The idea is that right when a Nextor 3 kernel boot sequence starts, and as soon as it detects that it is the first kernel in the system (or it's taking over a Nextor 2 or a MSX-DOS kernel) and if the user is pressing the N key, it displays a boot menu similar to this: ``` Nextor 3.0.0 boot menu diff --git a/source/kernel/Makefile b/source/kernel/Makefile index 01cc1db0..3cac9435 100644 --- a/source/kernel/Makefile +++ b/source/kernel/Makefile @@ -185,7 +185,7 @@ drivers/StandaloneASCII8/Nextor-$(VERSION).StandaloneASCII8.ROM: \ 256.bytes cat 256.bytes drivers/StandaloneASCII8/driver.bin > drivers/StandaloneASCII8/_driver.bin - $(MKNEXROM) nextor_base.dat $@ /d:drivers/StandaloneASCII8/driver.bin /m:drivers/StandaloneASCII8/chgbnk.bin + $(MKNEXROM) nextor_base.dat $@ /d:drivers/StandaloneASCII8/_driver.bin /m:drivers/StandaloneASCII8/chgbnk.bin $(call copy_to_bin,$@) drivers/StandaloneASCII8/driver.bin: \ diff --git a/source/kernel/bank0/init.mac b/source/kernel/bank0/init.mac index 193a7e0b..c60e3841 100644 --- a/source/kernel/bank0/init.mac +++ b/source/kernel/bank0/init.mac @@ -865,7 +865,12 @@ INIT2: ;the disk hooks set up by MSX-DOS compatible kernels. call SCANKEYS_RAM_AVAILABLE - jr z,AFTER_BOOT_MENU ;Keys came from RAM, flag preserved + jr nz,BM_NOT_FROM_RAM ;Keys came from keyboard + xor a + ld (ALL_KERNELS_DISABLED##),a + jr AFTER_BOOT_MENU + +BM_NOT_FROM_RAM: ld a,(BOOTKEYS##+2) and 80h diff --git a/source/kernel/bank4/bootmenu.mac b/source/kernel/bank4/bootmenu.mac index ddec94ad..52c62bac 100644 --- a/source/kernel/bank4/bootmenu.mac +++ b/source/kernel/bank4/bootmenu.mac @@ -1233,9 +1233,7 @@ PRINT_CENTERED_TITLE: inc de ;Append " boot menu" - push de ld hl,STR_BOOT_MENU_SUFFIX - pop de call COPY_STRING ;Calculate centering diff --git a/source/kernel/bank4/bootmsg.mac b/source/kernel/bank4/bootmsg.mac index 62d98c92..ed2c6633 100644 --- a/source/kernel/bank4/bootmsg.mac +++ b/source/kernel/bank4/bootmsg.mac @@ -59,6 +59,7 @@ PKM_SKIPPING: ld de,STR_SKIPPING_NEXTOR call PKM_PRINT_STR call PKM_PRINT_SLOT + call PKM_PRINT_CRLF jr PKM_CRLF PKM_INITIALIZING: @@ -73,10 +74,7 @@ PKM_INITIALIZING: pop bc push bc call PKM_PRINT_SLOT - ld a,13 - printchar - ld a,10 - printchar + call PKM_PRINT_CRLF ld b,4 PKM_INIT_SPC: ld a,' ' @@ -132,13 +130,11 @@ PKM_PRINT_NAME: call PKM_PRINT_STR PKM_CRLF: - ld a,13 - printchar - ld a,10 - printchar + call PKM_PRINT_CRLF ld a,(LINL40) cp 80 ret z +PKM_PRINT_CRLF: ld a,13 printchar ld a,10 diff --git a/source/kernel/drivers/StandaloneASCII8/driver.mac b/source/kernel/drivers/StandaloneASCII8/driver.mac index 5e5c97d1..4c3ab509 100644 --- a/source/kernel/drivers/StandaloneASCII8/driver.mac +++ b/source/kernel/drivers/StandaloneASCII8/driver.mac @@ -95,8 +95,7 @@ GWORK equ 4045h ;*** DRIVER CODE *** ;********************* - org 4000h - ds 256 + org 4100h DRIVER_START: