3 Commits
v1.01 ... main

Author SHA1 Message Date
Philip Smart
5b004979d5 SFD700 Version: Updated to have MZ700 and MZ80A drivers selected based on jumper pin 2026-04-03 14:20:43 +01:00
Philip Smart
3f5216dd2b SFD700 bug fixes 2026-04-01 23:29:33 +01:00
Philip Smart
dfb82db143 Added RFS versions of BASIC MZ-5Z008 and 2Z009E 2026-04-01 17:16:00 +01:00
42 changed files with 8870 additions and 41170 deletions

View File

@@ -112,7 +112,6 @@ HWSEL21: OUT (REG_FXXX),A
NOP NOP
NOP NOP
NOP NOP
NOP
ENDIF ENDIF
ENDM ENDM
@@ -136,9 +135,9 @@ BNKSWSEL: MACRO
ENDIF ENDIF
IF BUILD_SFD700 = 1 IF BUILD_SFD700 = 1
OUT (REG_EXXX),A ; Execute active bank switch for E000:EFFF. OUT (REG_EXXX),A ; Execute active bank switch for E000:EFFF.
CP ROMBANK6 ; ROMBANK6/7 page in ROM from E300:FFFF CP ROMBANK6 ; ROMBANK6:11 page in ROM from E300:FFFF
JR C,BNKSWJMP JR C,BNKSWJMP
INC A ; FXXX are 4K banks, EXXX are 2K banks. INC A ; FXXX are 4K banks, EXXX are 4K banks.
JR BNKSWJMP2 JR BNKSWJMP2
BNKSWJMP: LD A,(ROMBK1) ; All other banks place the current active ROM into F000:FXXX space. BNKSWJMP: LD A,(ROMBK1) ; All other banks place the current active ROM into F000:FXXX space.
BNKSWJMP2: OUT (REG_FXXX),A BNKSWJMP2: OUT (REG_FXXX),A

View File

@@ -51,8 +51,8 @@ VIDEOMODULE_ENA EQU 0 ; Targe
; Only enable one BUILD_<> operative. Customisations applied according ; Only enable one BUILD_<> operative. Customisations applied according
; to the target. ; to the target.
BUILD_ROMDISK EQU 0 ; RFS is built for the MZ-80A RomDisk card. BUILD_ROMDISK EQU 0 ; RFS is built for the MZ-80A RomDisk card.
BUILD_SFD700 EQU 0 ; RFS is built for the SFD700 Floppy Disk Controller. BUILD_SFD700 EQU 1 ; RFS is built for the SFD700 Floppy Disk Controller.
BUILD_PICOZ80 EQU 1 ; RFS is built for the picoZ80 board. BUILD_PICOZ80 EQU 0 ; RFS is built for the picoZ80 board.
BUILD_MZ80A EQU 0 ; RFS is customised to operate on an MZ-80A. BUILD_MZ80A EQU 0 ; RFS is customised to operate on an MZ-80A.
BUILD_MZ700 EQU 1 ; RFS is customised to operate on an MZ-700. BUILD_MZ700 EQU 1 ; RFS is customised to operate on an MZ-700.
@@ -284,7 +284,6 @@ MMIO7 EQU 0E7H ; MZ-70
; ;
REG_EXXX EQU 060H ; A write copies D6:0 into the EXXX page address register to set a uniform 4K block in the region E300:EFFF window. REG_EXXX EQU 060H ; A write copies D6:0 into the EXXX page address register to set a uniform 4K block in the region E300:EFFF window.
REG_FXXX EQU 061H ; A write copies D6:0 into the FXXX page address register to set a uniform 4k block in the region F000:FFFF. REG_FXXX EQU 061H ; A write copies D6:0 into the FXXX page address register to set a uniform 4k block in the region F000:FFFF.
REG_MEMMODE EQU 062H ; A write with D0 = low enables FlashROM, D0 = high enables RAM.
SFD700_MODE EQU 063H ; FDC Interface card configured target mode. SFD700_MODE EQU 063H ; FDC Interface card configured target mode.
FDC_CMD EQU 0D8H ; WD1773 Command Register. FDC_CMD EQU 0D8H ; WD1773 Command Register.
FDC_STATUS EQU 0D8H ; WD1773 Status Register. FDC_STATUS EQU 0D8H ; WD1773 Status Register.
@@ -421,14 +420,14 @@ ROMBANK4 EQU 4 + BNKDEFUROM ;
ROMBANK5 EQU 5 + BNKDEFUROM ; : RFS Bank 5 ROMBANK5 EQU 5 + BNKDEFUROM ; : RFS Bank 5
ROMBANK6 EQU 6 + BNKDEFUROM ; : RFS Bank 6 ROMBANK6 EQU 6 + BNKDEFUROM ; : RFS Bank 6
ROMBANK7 EQU 8 + BNKDEFUROM ; : RFS Bank 7 - Memory and timer test utilities. ROMBANK7 EQU 8 + BNKDEFUROM ; : RFS Bank 7 - Memory and timer test utilities.
ROMBANK8 EQU 8 + BNKDEFUROM ; Free : RFS Bank 8 - ROMBANK8 EQU 10 + BNKDEFUROM ; Free : RFS Bank 8 -
ROMBANK9 EQU 9 + BNKDEFUROM ; Free : RFS Bank 9 - ROMBANK9 EQU 12 + BNKDEFUROM ; Free : RFS Bank 9 -
ROMBANK10 EQU 10 + BNKDEFUROM ; Free : RFS Bank 10 - ROMBANK10 EQU 14 + BNKDEFUROM ; Free : RFS Bank 10 -
ROMBANK11 EQU 11 + BNKDEFUROM ; Free : RFS Bank 11 - ROMBANK11 EQU 16 + BNKDEFUROM ; Free : RFS Bank 11 -
ROMBANK12 EQU 12 + BNKDEFUROM ; Free : CBIOS Bank 1 - Utilities ROMBANK12 EQU 17 + BNKDEFUROM ; Free : CBIOS Bank 1 - Utilities
ROMBANK13 EQU 13 + BNKDEFUROM ; Free : CBIOS Bank 2 - Screen / ANSI Terminal ROMBANK13 EQU 18 + BNKDEFUROM ; Free : CBIOS Bank 2 - Screen / ANSI Terminal
ROMBANK14 EQU 14 + BNKDEFUROM ; Free : CBIOS Bank 3 - SD Card ROMBANK14 EQU 19 + BNKDEFUROM ; Free : CBIOS Bank 3 - SD Card
ROMBANK15 EQU 15 + BNKDEFUROM ; Free : CBIOS Bank 4 - Floppy disk controller. ROMBANK15 EQU 20 + BNKDEFUROM ; Free : CBIOS Bank 4 - Floppy disk controller.
ENDIF ENDIF
; MMC/SD command (SPI mode) ; MMC/SD command (SPI mode)

File diff suppressed because it is too large Load Diff

12
asm/mz2z009e_orig.asm Normal file
View File

@@ -0,0 +1,12 @@
; ===========================================================================
; Build configuration — set these flags to select the build variant:
;
; BUILD_RFS=0, BUILD_TAPE=0 : Original BASIC (QD boot, no RFS)
; BUILD_RFS=0, BUILD_TAPE=1 : Original BASIC (Tape/CMT/RFS ROM boot)
; BUILD_RFS=1, BUILD_TAPE=0 : RFS Enhanced BASIC (QD boot)
; BUILD_RFS=1, BUILD_TAPE=1 : RFS Enhanced BASIC (Tape/CMT/RFS ROM boot)
; ===========================================================================
BUILD_RFS EQU 0 ; 0 = original BASIC, 1 = RFS enhancements
BUILD_TAPE EQU 1 ; 0 = QD boot, 1 = Tape/CMT/RFS ROM boot
INCLUDE "mz2z009e.asm"

12
asm/mz2z009e_rfs.asm Normal file
View File

@@ -0,0 +1,12 @@
; ===========================================================================
; Build configuration — set these flags to select the build variant:
;
; BUILD_RFS=0, BUILD_TAPE=0 : Original BASIC (QD boot, no RFS)
; BUILD_RFS=0, BUILD_TAPE=1 : Original BASIC (Tape/CMT/RFS ROM boot)
; BUILD_RFS=1, BUILD_TAPE=0 : RFS Enhanced BASIC (QD boot)
; BUILD_RFS=1, BUILD_TAPE=1 : RFS Enhanced BASIC (Tape/CMT/RFS ROM boot)
; ===========================================================================
BUILD_RFS EQU 1 ; 0 = original BASIC, 1 = RFS enhancements
BUILD_TAPE EQU 1 ; 0 = QD boot, 1 = Tape/CMT/RFS ROM boot
INCLUDE "mz2z009e.asm"

File diff suppressed because it is too large Load Diff

12
asm/mz5z008_orig.asm Normal file
View File

@@ -0,0 +1,12 @@
; ===========================================================================
; Build configuration — set these flags to select the build variant:
;
; BUILD_RFS=0, BUILD_TAPE=0 : Original BASIC (QD boot, no RFS)
; BUILD_RFS=0, BUILD_TAPE=1 : Original BASIC (Tape/CMT/RFS ROM boot)
; BUILD_RFS=1, BUILD_TAPE=0 : RFS Enhanced BASIC (QD boot)
; BUILD_RFS=1, BUILD_TAPE=1 : RFS Enhanced BASIC (Tape/CMT/RFS ROM boot)
; ===========================================================================
BUILD_RFS EQU 0 ; 0 = original BASIC, 1 = RFS enhancements
BUILD_TAPE EQU 1 ; 0 = QD boot, 1 = Tape/CMT/RFS ROM boot
INCLUDE "mz5z008.asm"

File diff suppressed because it is too large Load Diff

View File

@@ -48,6 +48,125 @@
; Bring in additional resources. ; Bring in additional resources.
INCLUDE "rfs_definitions.asm" INCLUDE "rfs_definitions.asm"
;============================================================
;
; SFD700 COMMAND TABLE
;
; Placed after all bank includes so DW forward references to
; functions in various IF blocks are resolved as backward refs.
;============================================================
IF BUILD_SFD700 = 1
ORG 0E000H
ALIGN 0E300H
ENDIF
; Monitor command table (SFD700). This table contains the list of recognised commands along with the
; handler function and bank in which it is located.
;
; 7 6:3 2:0
; MATCH BANK SIZE
CMDTABLE2: IF BUILD_SFD700 = 1
DB 000H | 038H | 003H
DB "ASM" ; Assembler.
DW ASM_MAIN
DB 000H | 000H | 005H
DB "BASIC" ; Load and run BASIC SA-5510.
DW LOADBASIC
DB 000H | 020H | 001H
DB 'B' ; Bell.
DW SGX
DB 000H | 000H | 003H
DB "CPM" ; Load and run CPM.
DW LOADCPM
DB 000H | 018H | 002H
DB "CP" ; Copy Memory.
DW MCOPY
DB 000H | 018H | 003H
DB "DUC" ; Dump SD Card file contents (hex or text).
DW DUMPSDCARD
DB 000H | 018H | 001H
DB 'C' ; Clear Memory.
DW INITMEMX
DB 000H | 038H | 004H
DB "DASM" ; Disassembler.
DW DASM_MAIN
DB 000H | 018H | 001H
DB 'D' ; Dump Memory.
DW DUMPX
DB 000H | 018H | 002H
DB "FC" ; Save memory to Floppy.
DW SAVEFDCARD
DB 000H | 008H | 002H
DB "FL" ; 'FL' Floppy disk boot (built-in WD1773 FDC).
DW FLOPPY
DB 000H | 018H | 004H
DB "FD2T" ; Copy Floppy to Tape.
DW FD2TAPE
DB 000H | 008H | 002H
DB "FD" ; 'FD' Floppy disk directory listing.
DW FDDIR
DB 000H | 058H | 001H
DB 'H' ; Help screen (bank 11).
DW HELP
DB 000H | 018H | 002H ; Bank 3, 2-char command.
DB "IN" ; I/O port input.
DW INX
DB 000H | 000H | 002H
DB "IR" ; List ROM directory.
DW DIRROM
DB 000H | 000H | 001H
DB 'J' ; Jump to address.
DW GOTOX
DB 000H | 020H | 004H
DB "LTNX" ; Load from CMT without auto execution.
DW LOADTAPENX
DB 000H | 020H | 002H
DB "LT" ; Load from CMT
DW LOADTAPE
DB 000H | 000H | 004H
DB "LRNX" ; Load from ROM without auto execution.
DW LOADROMNX
DB 000H | 000H | 002H
DB "LR" ; Load from ROM
DW LOADROM
DB 000H | 020H | 001H
DB "L" ; Original Load from CMT
DW LOADTAPE
DB 000H | 018H | 001H
DB 'M' ; Edit Memory.
DW MCORX
DB 000H | 018H | 003H ; Bank 3, 3-char command.
DB "OUT" ; I/O port output.
DW OUTX
DB 000H | 018H | 001H
DB 'P' ; Printer test.
DW PTESTX
DB 000H | 020H | 001H
DB 'R' ; Memory test.
DW MEMTEST
DB 000H | 020H | 002H
DB "ST" ; Save to CMT
DW SAVEX
DB 000H | 020H | 001H
DB 'S' ; Save to CMT
DW SAVEX
DB 000H | 018H | 004H
DB "T2FD" ; Copy Tape to Floppy.
DW TAPE2FD
DB 000H | 000H | 004H
DB "TEST" ; A test function used in debugging.
DW LOCALTEST
DB 000H | 020H | 001H
DB 'T' ; Timer test.
DW TIMERTST
DB 000H | 000H | 001H
DB 'V' ; Verify CMT Save.
DW VRFYX
DB 0FFH ; END OF TABLE
ALIGN UROMADDR
ENDIF
;============================================================ ;============================================================
; ;
; USER ROM BANK 0 - Main RFS Entry point and functions. ; USER ROM BANK 0 - Main RFS Entry point and functions.
@@ -461,15 +580,19 @@ CMDTABLE: IF BUILD_ROMDISK+BUILD_PICOZ80 = 1
DB 000H | 010H | 002H DB 000H | 010H | 002H
DB "EC" ; Erase file. DB "EC" ; Erase file.
DW ERASESD DW ERASESD
IF BUILD_MZ700 = 1
DB 000H | 008H | 002H DB 000H | 008H | 002H
DB "FL" ; 'FL' RFS Floppy load/boot. DB "FL" ; 'FL' Floppy boot (built-in MZ-700 WD1773).
DW FLOPPY DW FLOPPY
DB 000H | 008H | 002H DB 000H | 008H | 002H
DB "FD" ; 'FD' Floppy directory. DB "FD" ; 'FD' Floppy directory.
DW FDDIR DW FDDIR
DB 000H | 008H | 001H ENDIF
DB 0AAH ; 'f' Original Floppy boot code. IF BUILD_MZ80A = 1
DB 000H | 008H | 002H
DB "FL" ; 'FL' Floppy boot (external FI ROM at F000H).
DW FDCK DW FDCK
ENDIF
DB 000H | 058H | 001H DB 000H | 058H | 001H
DB 'H' ; Help screen (bank 11). DB 'H' ; Help screen (bank 11).
DW HELP DW HELP
@@ -633,10 +756,10 @@ SETMODE80: IF BUILD_ROMDISK+BUILD_PICOZ80 = 1
JP MONIT JP MONIT
ENDIF ENDIF
NOTZPU: LDDE MSGNOTZINST ; No tranZPUter installed. ;NOTZPU: LDDE MSGNOTZINST ; No tranZPUter installed.
LD HL,PRINTMSG ; LD HL,PRINTMSG
CALL BKSW0to6 ; CALL BKSW0to6
RET ; RET
; The RFS depends on variables stored in unused parts of the Monitor scratch area. ; The RFS depends on variables stored in unused parts of the Monitor scratch area.
; When switching into a compatibility mode the memory is switched and these variables go ; When switching into a compatibility mode the memory is switched and these variables go
@@ -1206,9 +1329,11 @@ LOADBASIC: LD DE,BASICFILENM
ENDIF ; BUILD_SFD700 ENDIF ; BUILD_SFD700
IF BUILD_ROMDISK+BUILD_PICOZ80 = 1
LOADPROG: LD HL,LOADSDCARD LOADPROG: LD HL,LOADSDCARD
CALL BKSW0to2 CALL BKSW0to2
RET RET
ENDIF
;------------------------------------------------------------------------------- ;-------------------------------------------------------------------------------
; END OF RFS COMMAND FUNCTIONS. ; END OF RFS COMMAND FUNCTIONS.
@@ -1465,10 +1590,14 @@ DEFAULTFNE: EQU $
INCLUDE "rfs_utilities.asm" INCLUDE "rfs_utilities.asm"
; ;
; Ensure we fill the entire 2K by padding with FF's. ; Ensure we fill the entire 2K by padding with FF's.
; RomDisk/picoZ80: EFF8-EFFF are coded latch control registers.
; SFD700: no control registers, bank switching via I/O ports.
; ;
ALIGN 0EFF8h IF BUILD_ROMDISK+BUILD_PICOZ80 = 1
ORG 0EFF8h ALIGN 0EFF8h
DB 0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0AAh ORG 0EFF8h
DB 0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0AAh
ENDIF
IF BUILD_SFD700 = 1 IF BUILD_SFD700 = 1
ALIGN 0F000H ALIGN 0F000H
@@ -1490,111 +1619,3 @@ MEND:
INCLUDE "rfs_bank9.asm" INCLUDE "rfs_bank9.asm"
INCLUDE "rfs_bank10.asm" INCLUDE "rfs_bank10.asm"
INCLUDE "rfs_bank11.asm" INCLUDE "rfs_bank11.asm"
;============================================================
;
; SFD700 COMMAND TABLE
;
; Placed after all bank includes so DW forward references to
; functions in various IF blocks are resolved as backward refs.
;============================================================
IF BUILD_SFD700 = 1
ORG 0E000H
ALIGN 0E300H
ENDIF
; Monitor command table (SFD700). This table contains the list of recognised commands along with the
; handler function and bank in which it is located.
;
; 7 6:3 2:0
; MATCH BANK SIZE
CMDTABLE2: IF BUILD_SFD700 = 1
DB 000H | 038H | 003H
DB "ASM" ; Assembler.
DW ASM_MAIN
DB 000H | 000H | 005H
DB "BASIC" ; Load and run BASIC SA-5510.
DW LOADBASIC
DB 000H | 020H | 001H
DB 'B' ; Bell.
DW SGX
DB 000H | 000H | 003H
DB "CPM" ; Load and run CPM.
DW LOADCPM
DB 000H | 018H | 002H
DB "CP" ; Copy Memory.
DW MCOPY
DB 000H | 018H | 003H
DB "DUC" ; Dump SD Card file contents (hex or text).
DW DUMPSDCARD
DB 000H | 018H | 001H
DB 'C' ; Clear Memory.
DW INITMEMX
DB 000H | 038H | 004H
DB "DASM" ; Disassembler.
DW DASM_MAIN
DB 000H | 018H | 001H
DB 'D' ; Dump Memory.
DW DUMPX
DB 000H | 008H | 002H
DB "FL" ; 'FL' RFS Floppy load.
DW FDCK
; NB: FD/FDDIR command not available on SFD700 — FDDIR only exists
; for ROMDISK/picoZ80 builds (MZ-700 WD1773 FDC). SFD700 uses
; AFI ROM at F000 via FDCK for floppy operations.
DB 000H | 058H | 001H
DB 'H' ; Help screen (bank 11).
DW HELP
DB 000H | 018H | 002H ; Bank 3, 2-char command.
DB "IN" ; I/O port input.
DW INX
DB 000H | 000H | 002H
DB "IR" ; List ROM directory.
DW DIRROM
DB 000H | 000H | 001H
DB 'J' ; Jump to address.
DW GOTOX
DB 000H | 020H | 004H
DB "LTNX" ; Load from CMT without auto execution.
DW LOADTAPENX
DB 000H | 020H | 002H
DB "LT" ; Load from CMT
DW LOADTAPE
DB 000H | 000H | 004H
DB "LRNX" ; Load from ROM without auto execution.
DW LOADROMNX
DB 000H | 000H | 002H
DB "LR" ; Load from ROM
DW LOADROM
DB 000H | 020H | 001H
DB "L" ; Original Load from CMT
DW LOADTAPE
DB 000H | 018H | 001H
DB 'M' ; Edit Memory.
DW MCORX
DB 000H | 018H | 003H ; Bank 3, 3-char command.
DB "OUT" ; I/O port output.
DW OUTX
DB 000H | 018H | 001H
DB 'P' ; Printer test.
DW PTESTX
DB 000H | 020H | 001H
DB 'R' ; Memory test.
DW MEMTEST
DB 000H | 020H | 002H
DB "ST" ; Save to CMT
DW SAVEX
DB 000H | 020H | 001H
DB 'S' ; Save to CMT
DW SAVEX
DB 000H | 000H | 004H
DB "TEST" ; A test function used in debugging.
DW LOCALTEST
DB 000H | 020H | 001H
DB 'T' ; Timer test.
DW TIMERTST
DB 000H | 000H | 001H
DB 'V' ; Verify CMT Save.
DW VRFYX
DB 0FFH ; END OF TABLE
ENDIF

View File

@@ -145,6 +145,14 @@ TRK0FD3 EQU 01004H
TRK0FD4 EQU 01005H TRK0FD4 EQU 01005H
RETRIES EQU 01006H RETRIES EQU 01006H
BPARA EQU 01008H BPARA EQU 01008H
; SAVEFDX work variables (after BPARA's 11 bytes at 1008H-1012H).
FREEDSEC EQU 01014H ; Logical sector of free dir entry.
FREEDDIX EQU 01016H ; Index (0-7) of free entry in sector.
NXTDATASEC EQU 01017H ; Next free data sector (2 bytes LE).
SAVEDRVNO EQU 01019H ; Saved drive number for save operations.
; Temporary variables for TMPSTACKP save/restore (MUST be in RAM, not ROM).
FD7RS_TSP EQU 0101AH ; FD7READSAFE saved TMPSTACKP (2 bytes).
FD7ERR_TSP EQU 0101CH ; FD7ERR saved TMPSTACKP (2 bytes).
;------------------------------------------------------------------------------- ;-------------------------------------------------------------------------------
; START OF FLOPPY DISK CONTROLLER FUNCTIONALITY ; START OF FLOPPY DISK CONTROLLER FUNCTIONALITY
@@ -159,8 +167,13 @@ FDCKROM: LD A,(0F000h)
OR A OR A
RET RET
FLOPPY: IF BUILD_ROMDISK+BUILD_PICOZ80 = 1 FLOPPY: IF BUILD_ROMDISK+BUILD_PICOZ80+BUILD_SFD700 = 1
IF BUILD_MZ80A = 1 ;
; NOTE: MZ-80A FDC code removed from bank 1.
; - SFD700+MZ80A: handled by bank 8 (FD80A_BOOT) with HW acceleration.
; - ROMDISK/picoZ80+MZ80A: uses physical MZ-80A FI ROM at F000H via FDCK.
;
IF 0 = 1 ; MZ-80A section removed.
PUSH DE ; Preserve pointer to input buffer. PUSH DE ; Preserve pointer to input buffer.
LD DE,BPARA ; Copy disk parameter block into RAM work area. (From) LD DE,BPARA ; Copy disk parameter block into RAM work area. (From)
LD HL,PRMBLK ; (To) LD HL,PRMBLK ; (To)
@@ -545,8 +558,24 @@ L0300: IN A,(0D8H) ; State
; RETRIES (1006H): Retry counter ; RETRIES (1006H): Retry counter
;------------------------------------------------------------------------------- ;-------------------------------------------------------------------------------
; Method to boot from MZ-700 floppy disk. ; Boot entry. MZ-80A mode dispatch temporarily disabled for testing.
; ; Initialize timer (as MZ-1E05 does) and reset FDC.
PUSH DE ; Save command line pointer.
XOR A
LD DE,0
CALL ?TMST ; Timer init.
POP DE ; Restore command line pointer.
XOR A
OUT (FDC_DRIVE),A ; Deselect all drives, motor off.
LD A,0D8H ; Force interrupt.
CPL
OUT (FDC_CMD),A
LD B,0 ; ~1.7ms delay for FDC to settle.
FLDLY1: DJNZ FLDLY1
XOR A
LD (MOTON),A
LD (TRK0FD1),A
LD (FDCCMD),A
PUSH DE ; Preserve pointer to input buffer. PUSH DE ; Preserve pointer to input buffer.
LD DE,BPARA ; Copy disk parameter block into RAM work area. LD DE,BPARA ; Copy disk parameter block into RAM work area.
LD HL,PRMBLK LD HL,PRMBLK
@@ -556,7 +585,6 @@ L0300: IN A,(0D8H) ; State
LD A,(DE) ; Check if drive number given on command line. LD A,(DE) ; Check if drive number given on command line.
CP 00DH CP 00DH
JR NZ,FD7BOOT JR NZ,FD7BOOT
CALL FD7INIT ; Initialise disk and flags.
FD7PRMPT: LDDE MSGBOOTDRV FD7PRMPT: LDDE MSGBOOTDRV
LD HL,PRINTMSG LD HL,PRINTMSG
CALL BKSW1to6 CALL BKSW1to6
@@ -602,6 +630,7 @@ FD7CHK: LD C,(HL)
CALL BKSW1to6 CALL BKSW1to6
; ;
; Extract load parameters from boot sector. ; Extract load parameters from boot sector.
LD IX,BPARA ; Restore IX (PRINTMSG may corrupt it).
LD HL,(0CF16H) ; Load/target address. LD HL,(0CF16H) ; Load/target address.
LD A,H LD A,H
OR L OR L
@@ -649,12 +678,18 @@ FD7NOTMST: CALL FD7INIT
LDDE MSGDSKNOTMST LDDE MSGDSKNOTMST
JR FD7ERR JR FD7ERR
FD7LOADERR: LDDE MSGLOADERR FD7LOADERR: LDDE MSGLOADERR
FD7ERR: LD HL,PRINTMSG FD7ERR: LD HL,(TMPSTACKP) ; Save TMPSTACKP before BKSW overwrites it.
LD (FD7ERR_TSP),HL
LD HL,PRINTMSG
CALL BKSW1to6 CALL BKSW1to6
LD DE,ERRTONE LD DE,ERRTONE
CALL MELDY CALL MELDY
LD SP,(TMPSTACKP) ; Recover stack pointer. LD HL,(FD7ERR_TSP) ; Restore saved TMPSTACKP.
LD SP,HL ; Recover stack pointer.
RET RET
; FD7ERR_TSP now in RAM (EQU 0101CH).
;------------------------------------------------------------------------------- ;-------------------------------------------------------------------------------
; MZ-700 FDC LOW-LEVEL ROUTINES ; MZ-700 FDC LOW-LEVEL ROUTINES
@@ -687,6 +722,7 @@ FD7INIT: PUSH AF
; Motor on: enable motor and wait for spinup. ; Motor on: enable motor and wait for spinup.
FD7MOTON: LD A,080H FD7MOTON: LD A,080H
OUT (FDC_DRIVE),A OUT (FDC_DRIVE),A
CALL FD7DLY80U ; Settle delay after motor on.
LD B,16 LD B,16
FD7MTD1: CALL FD7DLY60M FD7MTD1: CALL FD7DLY60M
DJNZ FD7MTD1 DJNZ FD7MTD1
@@ -701,6 +737,7 @@ FD7READY: LD A,(MOTON)
LD A,(IX+0) ; Drive number. LD A,(IX+0) ; Drive number.
OR 084H OR 084H
OUT (FDC_DRIVE),A ; Drive select + motor on. OUT (FDC_DRIVE),A ; Drive select + motor on.
CALL FD7DLY80U ; Settle delay after drive select.
XOR A XOR A
LD (FDCCMD),A LD (FDCCMD),A
CALL FD7DLY60M CALL FD7DLY60M
@@ -798,6 +835,33 @@ FD7BSF3: DEC E
POP DE POP DE
JP FD7DSKERR JP FD7DSKERR
;-------------------------------------------------------------------------------
; FD7READSAFE - Safe read wrapper. Catches FD7ERR longjmp and returns
; with carry set instead of corrupting the caller's stack.
; IX = parameter block (BPARA). Returns: carry clear=OK, carry set=error.
; Mechanism: pushes a recovery address on the stack, then sets TMPSTACKP
; to SP. FD7ERR's "LD SP,(TMPSTACKP) / RET" pops the recovery address,
; landing at FD7RS_REC instead of unwinding the entire call chain.
;-------------------------------------------------------------------------------
FD7READSAFE: LD HL,(TMPSTACKP)
LD (FD7RS_TSP),HL ; Save original TMPSTACKP.
LD HL,FD7RS_REC ; Recovery address.
PUSH HL ; Push onto stack.
LD (TMPSTACKP),SP ; FD7ERR longjmp lands here → pops FD7RS_REC.
CALL FD7READ ; Normal return if success.
POP HL ; Remove recovery address.
LD HL,(FD7RS_TSP)
LD (TMPSTACKP),HL ; Restore TMPSTACKP.
OR A ; Clear carry = success.
RET
; FD7ERR longjmp caught: SP restored, RET popped FD7RS_REC → here.
; Error message and tone already displayed by FD7ERR.
FD7RS_REC: LD HL,(FD7RS_TSP)
LD (TMPSTACKP),HL ; Restore TMPSTACKP.
SCF ; Set carry = error.
RET
; FD7RS_TSP now in RAM (EQU 0101AH).
; Sequential read from disk. ; Sequential read from disk.
; IX = parameter block (BPARA). ; IX = parameter block (BPARA).
FD7READ: CALL FD7CNVRT ; Convert logical sector to track/sector. FD7READ: CALL FD7CNVRT ; Convert logical sector to track/sector.
@@ -952,7 +1016,22 @@ FD7DLYT: DEC DE
; Input: DE = pointer to optional drive number on command line. ; Input: DE = pointer to optional drive number on command line.
;------------------------------------------------------------------------------- ;-------------------------------------------------------------------------------
FDDIR: ; Parse drive number (default = drive 1 = index 0). FDDIR: ; Initialize timer and reset FDC.
XOR A
LD DE,0
CALL ?TMST ; Timer init.
XOR A
OUT (FDC_DRIVE),A ; Deselect all drives, motor off.
LD A,0D8H ; Force interrupt.
CPL
OUT (FDC_CMD),A
LD B,0 ; ~1.7ms delay.
FDDLY1: DJNZ FDDLY1
XOR A
LD (MOTON),A
LD (TRK0FD1),A
LD (FDCCMD),A
; Parse drive number (default = drive 1 = index 0).
LD A,(DE) LD A,(DE)
CP 00DH CP 00DH
JR Z,FDDIR_DEF JR Z,FDDIR_DEF
@@ -990,6 +1069,7 @@ FDDIR_GO: LD (BPARA),A ; Store
; Read boot sector (sector 0 = IPL) and verify signature. ; Read boot sector (sector 0 = IPL) and verify signature.
LD IX,BPARA LD IX,BPARA
CALL FD7READ CALL FD7READ
JP C,FDDIR_END ; Read error.
LD HL,0CF00H LD HL,0CF00H
LD DE,DSKID LD DE,DSKID
LD B,7 LD B,7
@@ -1003,11 +1083,11 @@ FDDIR_IPL: LD A,(DE)
LD DE,0CF07H LD DE,0CF07H
LD HL,PRTFN LD HL,PRTFN
CALL BKSW1to6 CALL BKSW1to6
LD HL,(0CF16H) LD HL,(0CF18H) ; Exec addr.
PUSH HL PUSH HL ; Stack bottom: exec.
LD HL,(0CF18H) LD HL,(0CF16H) ; Load addr.
PUSH HL PUSH HL ; Stack top: load.
LD BC,(0CF14H) LD BC,(0CF14H) ; Size.
LDDE MSGFDINFO LDDE MSGFDINFO
LD HL,PRINTMSG LD HL,PRINTMSG
CALL BKSW1to6 CALL BKSW1to6
@@ -1022,17 +1102,19 @@ FDDIR_IPL: LD A,(DE)
; Get number of directory sectors from IPL offset +1EH. ; Get number of directory sectors from IPL offset +1EH.
LD A,(0CF1EH) ; Start data sector (low byte). LD A,(0CF1EH) ; Start data sector (low byte).
SUB 16 ; Directory sectors = startDataSec - 16. SUB 16 ; Directory sectors = startDataSec - 16.
JR Z,FDDIR_END ; No directory sectors. JP Z,FDDIR_END ; No directory sectors.
JR C,FDDIR_END JP C,FDDIR_END
LD C,A ; C = number of directory sectors. LD C,A ; C = number of directory sectors.
LD A,16 ; First directory sector. LD A,16 ; First directory sector.
; ;
FDDIR_SEC: ; Read next directory sector. FDDIR_SEC: ; Read next directory sector.
PUSH AF ; Save current sector number. PUSH AF ; Save current sector number.
PUSH BC ; Save remaining sector count. PUSH BC ; Save remaining sector count.
LD IX,BPARA ; Restore IX (may be corrupted by PRINTMSG).
LD (IX+1),A LD (IX+1),A
LD (IX+2),0 LD (IX+2),0
CALL FD7READ CALL FD7READ
JR C,FDDIR_RERR ; Read error → clean stack and exit.
; ;
; First sector (16): skip entry 0 (FAT header), scan entries 1-7. ; First sector (16): skip entry 0 (FAT header), scan entries 1-7.
; Subsequent sectors: scan all 8 entries. ; Subsequent sectors: scan all 8 entries.
@@ -1048,12 +1130,25 @@ FDDIR_SEC: ; Read next directory sector.
FDDIR_AL8: LD HL,0CF00H ; Start from entry 0. FDDIR_AL8: LD HL,0CF00H ; Start from entry 0.
LD B,8 ; 8 entries per sector. LD B,8 ; 8 entries per sector.
; ;
FDDIR_ENT: ; Check type byte at (HL). 00 = empty/deleted, FF = unused → skip. FDDIR_ENT: ; Check type byte at (HL). Only 01-7F are valid file entries.
LD A,(HL) LD A,(HL)
OR A OR A
JR Z,FDDIR_NXE JR Z,FDDIR_NXE ; 00 = deleted/empty.
CP 080H
JR NC,FDDIR_NXE ; >= 80H = header/system/uninit.
; Validate: check SectorAddress is not 0000 or FFFF (uninit garbage).
PUSH HL
LD DE,01EH
ADD HL,DE
LD A,(HL)
INC HL
LD D,(HL) ; D=high, A=low of SectorAddress.
POP HL
OR D
JR Z,FDDIR_NXE ; SectorAddress=0000 → skip.
AND D
CP 0FFH CP 0FFH
JR Z,FDDIR_NXE JR Z,FDDIR_NXE ; SectorAddress=FFFF → skip.
; ;
; Valid entry: print filename and load/exec/size. ; Valid entry: print filename and load/exec/size.
LD (TMPADR),HL ; Save entry base. LD (TMPADR),HL ; Save entry base.
@@ -1063,23 +1158,27 @@ FDDIR_ENT: ; Check type byte at (HL). 00 = empty/deleted, FF = unused → skip
EX DE,HL EX DE,HL
LD HL,PRTFN LD HL,PRTFN
CALL BKSW1to6 CALL BKSW1to6
; Read size from base+12H, load from base+14H, exec from base+16H. ; Read size from +14H, load from +16H, exec from +18H.
; (Dir entry: +12=LockFlag, +13=DummyFlag, +14=FileSize,
; +16=LoadAddress, +18=ExecAddress, +1E=SectorAddress.)
LD HL,(TMPADR) LD HL,(TMPADR)
LD DE,012H LD DE,014H
ADD HL,DE ADD HL,DE
LD C,(HL) LD C,(HL)
INC HL INC HL
LD B,(HL) ; BC = size. LD B,(HL) ; BC = file size.
INC HL ; +14H = load addr. INC HL ; +16H = load addr.
LD E,(HL) LD E,(HL)
INC HL INC HL
LD D,(HL) LD D,(HL)
PUSH DE ; Stack: load addr. PUSH DE ; Save load addr.
INC HL ; +16H = exec addr. INC HL ; +18H = exec addr.
LD E,(HL) LD E,(HL)
INC HL INC HL
LD D,(HL) LD D,(HL) ; DE = exec addr.
PUSH DE ; Stack: exec addr. POP HL ; HL = load addr.
PUSH DE ; Stack bottom: exec.
PUSH HL ; Stack top: load (MSGFDINFO pops load first).
LDDE MSGFDINFO LDDE MSGFDINFO
LD HL,PRINTMSG LD HL,PRINTMSG
CALL BKSW1to6 CALL BKSW1to6
@@ -1098,10 +1197,474 @@ FDDIR_NXE: LD DE,32 ; Next 3
INC A ; Next sector. INC A ; Next sector.
DEC C DEC C
JR NZ,FDDIR_SEC ; More directory sectors. JR NZ,FDDIR_SEC ; More directory sectors.
JP FDDIR_END ; Done — skip error recovery POPs.
; ;
FDDIR_RERR: POP BC ; Clean stack from FDDIR_SEC PUSH.
POP AF
FDDIR_END: CALL FD7INIT FDDIR_END: CALL FD7INIT
RET RET
;-------------------------------------------------------------------------------
; FD7WRITE - Sequential write to disk.
; Mirror of FD7READ but uses OUTI for data output.
; IX = parameter block (BPARA): drive, logical sector, size, source addr.
;-------------------------------------------------------------------------------
; Sequential write to disk.
; Returns: carry clear = success, carry set = error.
; Does NOT call FD7DSKERR — caller handles errors via carry flag.
FD7WRITE: CALL FD7CNVRT ; Convert logical sector to track/sector.
CALL FD7PRST1 ; Setup params, HL = source address.
FD7WE8: CALL FD7SIDST ; Set side/head.
CALL FD7SEEK
JR NZ,FD7WERR ; Seek failed.
CALL FD7PRST2 ; Set track & sector registers.
DI
LD A,0A4H ; Write sector (multiple records).
CALL FD7CMD2
FD7WE6: LD B,0 ; 256 bytes per sector.
FD7WE4: IN A,(FDC_CMD)
RRCA
JR C,FD7WE3 ; Busy dropped = done.
RRCA
JR C,FD7WE4 ; DRQ not set, keep waiting.
OUTI ; Write byte: OUT(C)=(HL), B--, HL++.
JR NZ,FD7WE4
;
INC (IX+8) ; Next sector.
LD A,(IX+8)
CP 17 ; Past last sector on track?
JR Z,FD7WESEC
DEC D ; Decrement sector count.
JR NZ,FD7WE6 ; More sectors to write.
JR FD7WEDONE
FD7WESEC: DEC D
FD7WEDONE: LD A,0D8H ; Force interrupt.
CPL
OUT (FDC_CMD),A
CALL FD7BSYON
FD7WE3: EI
IN A,(FDC_CMD)
CPL
AND 07CH ; Check error bits (WP, WF, RNF, CRC, lost data).
JR NZ,FD7WERR ; Write error.
CALL FD7ADJ ; Adjust sector/track.
JR Z,FD7WEND ; All sectors written.
LD A,(IX+7) ; Next track.
JR FD7WE8
FD7WEND: LD A,080H
OUT (FDC_DRIVE),A ; Keep motor on.
OR A ; Clear carry = success.
RET
FD7WERR: EI
SCF ; Set carry = error.
RET
;-------------------------------------------------------------------------------
; LOADFDCP - Load file from floppy into memory, populating CMT header.
; Reads boot sector, extracts file info, reads program data to 0x1200.
; Sets RESULT=0 on success, non-zero on error.
; Called from bank 3 for FD2T command.
;-------------------------------------------------------------------------------
;-------------------------------------------------------------------------------
; LOADFDCP - Load FIRST file from floppy directory into memory at 0x1200.
; Scans directory for first valid entry, populates CMT header, loads data.
; Output: RESULT = 0 success, 0xFE FD7ERR handled, 0xFF caller shows error.
;-------------------------------------------------------------------------------
LOADFDCP: LD A,0FEH
LD (RESULT),A ; 0xFE = FD7ERR will handle errors.
; Full FDC init: timer, deselect, force interrupt, clear flags.
PUSH DE
XOR A
LD DE,0
CALL ?TMST
POP DE
XOR A
OUT (FDC_DRIVE),A
LD A,0D8H
CPL
OUT (FDC_CMD),A
LD B,0
LFDCP_DLY: DJNZ LFDCP_DLY
XOR A
LD (MOTON),A
LD (TRK0FD1),A
; Init BPARA.
LD HL,PRMBLK
LD DE,BPARA
LD BC,11
LDIR
LD IX,BPARA
;
; Scan directory for first valid file entry.
LD A,16 ; First directory sector.
LD C,16 ; 16 sectors.
LFDCP_DSC: PUSH AF
PUSH BC
LD (IX+1),A
LD (IX+2),0
LD HL,0CF00H
LD (IX+5),L
LD (IX+6),H
LD (IX+3),0
LD (IX+4),1
CALL FD7READ ; (FD7ERR longjumps on error.)
POP BC
POP AF
;
LD HL,0CF00H
LD B,8
LFDCP_ENT: LD A,(HL)
OR A
JR Z,LFDCP_NXE ; 00 = empty.
CP 080H
JR NC,LFDCP_NXE ; >=80 = header/system.
; Valid entry — check if filename matches NAME.
PUSH HL
PUSH BC
INC HL ; +01 = filename in entry.
LD DE,NAME
LD B,17 ; Compare 17 bytes.
LFDCP_CMP: LD A,(DE)
CP (HL)
JR NZ,LFDCP_NOM ; Mismatch.
CP 00DH ; Both end with CR?
JR Z,LFDCP_MAT ; Match!
INC HL
INC DE
DJNZ LFDCP_CMP
LFDCP_MAT: POP BC
POP HL
JP LFDCP_GOT ; Found matching file.
LFDCP_NOM: POP BC
POP HL
LFDCP_NXE: LD DE,32
ADD HL,DE
DJNZ LFDCP_ENT
; No entry in this sector, try next.
POP AF ; Wait, stack issue...
; Actually the POP AF/BC was already done above. Just continue.
INC A
DEC C
JP NZ,LFDCP_DSC
; No file found on disk.
JP LFDCP_ERR
;
LFDCP_GOT: ; HL = entry base of first valid file.
; Populate CMT header from directory entry.
LD A,(HL) ; +00 = FileType.
LD (ATRB),A
PUSH HL
INC HL ; +01 = Filename.
LD DE,NAME
LD BC,17
LDIR
POP HL
PUSH HL
LD DE,014H
ADD HL,DE ; +14 = FileSize.
LD E,(HL)
INC HL
LD D,(HL)
LD (SIZE),DE
INC HL ; +16 = LoadAddress.
LD E,(HL)
INC HL
LD D,(HL)
LD (DTADR),DE
INC HL ; +18 = ExecAddress.
LD E,(HL)
INC HL
LD D,(HL)
LD (EXADR),DE
POP HL
PUSH HL
LD DE,01EH
ADD HL,DE ; +1E = SectorAddress.
LD E,(HL)
INC HL
LD D,(HL)
POP HL
; DE = start sector, load data to 0x1200.
LD (IX+1),E
LD (IX+2),D
LD HL,(SIZE)
LD (IX+3),L
LD (IX+4),H
LD HL,01200H
LD (IX+5),L
LD (IX+6),H
LD (DTADR),HL ; Override load addr for copy.
CALL FD7READ
CALL FD7INIT
XOR A
LD (RESULT),A ; Success.
RET
LFDCP_ERR: CALL FD7INIT
LD A,0FFH
LD (RESULT),A
RET
;-------------------------------------------------------------------------------
; SAVEFDX - Save memory to floppy disk with IPL boot header.
; Writes an IPL boot sector at sector 0 and program data from sector 1+.
; Uses CMT header fields: NAME, SIZE, DTADR, EXADR.
; Called from bank 3 for T2FD and FC commands.
;-------------------------------------------------------------------------------
;-------------------------------------------------------------------------------
; SAVEFDX - Save file to floppy disk directory.
; Scans directory for a free entry, calculates next free data sector,
; writes program data, then updates the directory entry.
; Input: NAME, SIZE, DTADR, EXADR, ATRB set by caller.
; Output: RESULT = 0 on success, 0xFF on error.
;-------------------------------------------------------------------------------
SAVEFDX: LD A,0FFH
LD (RESULT),A ; Default = error.
; Full FDC init: timer, deselect, force interrupt, clear flags.
PUSH DE
XOR A
LD DE,0
CALL ?TMST ; Timer init (required for FDC).
POP DE
XOR A
OUT (FDC_DRIVE),A ; Deselect drives.
LD A,0D8H
CPL
OUT (FDC_CMD),A ; Force interrupt.
LD B,0
SFDX_DLY: DJNZ SFDX_DLY ; Settle delay.
XOR A
LD (MOTON),A
LD (TRK0FD1),A
; Init BPARA from PRMBLK and set drive 0.
LD HL,PRMBLK
LD DE,BPARA
LD BC,11
LDIR
LD IX,BPARA
; Init scan variables.
LD A,0FFH
LD (FREEDSEC),A ; FF = no free entry found yet.
LD HL,32 ; First data sector after directory.
LD (NXTDATASEC),HL
;
; --- Scan directory: logical sectors 16-31 (Track 0, Head 1). ---
LD A,16 ; First directory sector.
LD C,16 ; 16 sectors to scan.
SFDX_DSEC: LD (SAVEDRVNO),A ; Save current sector for SFDX_FREE.
PUSH AF
PUSH BC
LD IX,BPARA
LD (IX+1),A ; Logical sector.
LD (IX+2),0
LD HL,0CF00H ; Buffer.
LD (IX+5),L
LD (IX+6),H
LD (IX+3),0
LD (IX+4),1 ; 1 sector = 256 bytes.
CALL FD7READ
POP BC
POP AF
JP C,SFDX_ERR ; Read error.
PUSH AF
PUSH BC
;
; Scan 8 entries in this sector.
LD HL,0CF00H
LD B,8
SFDX_DENT: PUSH HL
PUSH BC
LD A,(HL) ; FileType.
OR A
JR Z,SFDX_FREE ; 00 = deleted/free entry.
CP 0FFH
JR Z,SFDX_FREE ; FF = uninitialized/free entry.
CP 080H
JR NC,SFDX_NXTE ; >=80H = header/system, skip.
; Check SectorAddress — if 0000 or FFFF, treat as free (uninit garbage).
PUSH HL
LD DE,01EH
ADD HL,DE
LD E,(HL)
INC HL
LD D,(HL) ; DE = SectorAddress.
POP HL
LD A,E
OR D
JR Z,SFDX_FREE ; SectorAddress=0000 → free.
LD A,E
AND D
CP 0FFH
JR Z,SFDX_FREE ; SectorAddress=FFFF → free.
PUSH HL
LD BC,014H
ADD HL,BC
LD C,(HL)
INC HL
LD B,(HL) ; BC = FileSize.
POP HL
; Sectors used = (FileSize + 255) / 256 = (FileSize >> 8) + ((FileSize & FF) ? 1 : 0).
LD A,C
OR A
JR Z,SFDX_NRU
INC B ; Round up.
SFDX_NRU: LD C,B
LD B,0 ; BC = sectors used.
EX DE,HL ; HL = SectorAddress.
ADD HL,BC ; HL = end sector.
EX DE,HL ; DE = end sector.
; Update NXTDATASEC if this file ends later.
PUSH HL
LD HL,(NXTDATASEC)
OR A
SBC HL,DE ; HL = NXTDATASEC - endSec.
POP HL
JR NC,SFDX_NXTE ; NXTDATASEC >= endSec, no update.
LD (NXTDATASEC),DE ; Update with higher value.
JR SFDX_NXTE
;
SFDX_FREE: ; Found a free entry. Record if first one found.
LD A,(FREEDSEC)
CP 0FFH
JR NZ,SFDX_NXTE ; Already have one.
; Record: FREEDSEC = current logical sector, FREEDDIX = entry index.
; Entry index = 8 - B (since B counts down from 8).
POP BC ; B = entries remaining.
POP HL
PUSH HL
PUSH BC
LD A,8
SUB B
LD (FREEDDIX),A
LD A,(SAVEDRVNO) ; Sector number saved at loop start.
LD (FREEDSEC),A
;
SFDX_NXTE: POP BC
POP HL
LD DE,32 ; Next 32-byte entry.
ADD HL,DE
DEC B
JR NZ,SFDX_DENT
;
POP BC
POP AF
INC A ; Next directory sector.
DEC C
JP NZ,SFDX_DSEC
;
; --- Check we found a free entry. ---
LD A,(FREEDSEC)
CP 0FFH
RET Z ; No free entry → error.
;
; --- Write file data at NXTDATASEC. ---
LD IX,BPARA
LD HL,(NXTDATASEC)
LD (IX+1),L
LD (IX+2),H
LD HL,(SIZE)
LD (IX+3),L
LD (IX+4),H
LD HL,(DTADR)
LD (IX+5),L
LD (IX+6),H
CALL FD7WRITE
JP C,SFDX_ERR
;
; --- Re-read directory sector with free entry. ---
LD IX,BPARA
LD A,(FREEDSEC)
LD (IX+1),A
LD (IX+2),0
LD HL,0CF00H
LD (IX+5),L
LD (IX+6),H
LD (IX+3),0
LD (IX+4),1
CALL FD7READ
JP C,SFDX_ERR ; Read error.
;
; --- Fill directory entry. ---
LD A,(FREEDDIX)
LD HL,0CF00H
OR A
JR Z,SFDX_FILL ; Index 0 → HL already at CF00.
LD DE,32
SFDX_FMUL: ADD HL,DE
DEC A
JR NZ,SFDX_FMUL
SFDX_FILL: ; HL = entry base. Clear 32 bytes.
PUSH HL
LD D,H
LD E,L
INC DE
LD (HL),0
LD BC,31
LDIR
POP HL
; +00: FileType from ATRB (with CMT→FD type conversion).
LD A,(ATRB)
CP 005H ; CMT type 05 (RB) → FD type 02 (BTX).
JR NZ,SFDX_FT1
LD A,002H
SFDX_FT1: LD (HL),A
; +01: FileName from NAME (17 bytes).
PUSH HL
INC HL
EX DE,HL
LD HL,NAME
LD BC,17
LDIR
POP HL
; +14: FileSize from SIZE.
PUSH HL
LD DE,014H
ADD HL,DE
LD DE,(SIZE)
LD (HL),E
INC HL
LD (HL),D
; +16: LoadAddress from DTADR.
INC HL
LD DE,(DTADR)
LD (HL),E
INC HL
LD (HL),D
; +18: ExecAddress from EXADR.
INC HL
LD DE,(EXADR)
LD (HL),E
INC HL
LD (HL),D
POP HL
; +1E: SectorAddress = NXTDATASEC.
PUSH HL
LD DE,01EH
ADD HL,DE
LD DE,(NXTDATASEC)
LD (HL),E
INC HL
LD (HL),D
POP HL
;
; --- Write directory sector back. ---
LD A,(FREEDSEC)
LD (IX+1),A
LD (IX+2),0
LD HL,0CF00H
LD (IX+5),L
LD (IX+6),H
LD (IX+3),0
LD (IX+4),1
CALL FD7WRITE
JP C,SFDX_ERR
;
CALL FD7INIT
XOR A
LD (RESULT),A ; Success.
RET
SFDX_ERR: CALL FD7INIT
RET ; RESULT still = 0xFF.
ENDIF ENDIF
ENDIF ENDIF
@@ -1119,19 +1682,27 @@ FDDIR_END: CALL FD7INIT
; Error tone. ; Error tone.
ERRTONE: DB "A0", 0D7H, "ARA", 0D7H, "AR", 00DH ERRTONE: DB "A0", 0D7H, "ARA", 0D7H, "AR", 00DH
; Boot disk identifier - 002H prefix for MZ-80A, 003H prefix for MZ-700. ; Boot disk identifier - prefix set at runtime for SFD700, compile-time otherwise.
DSKID: IF BUILD_MZ80A = 1 ; (Glass: label must be outside IF for global visibility.)
DB 002H, "IPLPRO" DSKID: IF BUILD_SFD700 = 1
DB 003H, "IPLPRO" ; Default MZ-700; compile-time value for BUILD_MZ700.
ELSE ELSE
DB 003H, "IPLPRO" IF BUILD_MZ80A = 1
DB 002H, "IPLPRO"
ELSE
DB 003H, "IPLPRO"
ENDIF
ENDIF ENDIF
; Parameter block: drive 0, sector 0, 256 bytes, load address differs. ; Parameter block: drive 0, sector 0, 256 bytes, load address set at runtime for SFD700.
; MZ-80A loads boot sector to CE00H, MZ-700 loads to CF00H. PRMBLK: IF BUILD_SFD700 = 1
PRMBLK: IF BUILD_MZ80A = 1 DB 000H, 000H, 000H, 000H, 001H, 000H, 0CFH, 000H, 000H, 000H, 000H ; Default MZ-700; patched at runtime.
DB 000H, 000H, 000H, 000H, 001H, 000H, 0CEH, 000H, 000H, 000H, 000H
ELSE ELSE
DB 000H, 000H, 000H, 000H, 001H, 000H, 0CFH, 000H, 000H, 000H, 000H IF BUILD_MZ80A = 1
DB 000H, 000H, 000H, 000H, 001H, 000H, 0CEH, 000H, 000H, 000H, 000H
ELSE
DB 000H, 000H, 000H, 000H, 001H, 000H, 0CFH, 000H, 000H, 000H, 000H
ENDIF
ENDIF ENDIF
IF BUILD_MZ80A = 1 IF BUILD_MZ80A = 1

View File

@@ -36,6 +36,13 @@
;- along with this program. If not, see <http://www.gnu.org/licenses/>. ;- along with this program. If not, see <http://www.gnu.org/licenses/>.
;-------------------------------------------------------------------------------------------------------- ;--------------------------------------------------------------------------------------------------------
IF BUILD_SFD700 = 1
ORG 0E000H
ALIGN 0E300H
DB "BANK10"
ALIGN UROMADDR
ENDIF
;=========================================================== ;===========================================================
; ;
; USER ROM BANK 10 - ; USER ROM BANK 10 -
@@ -144,6 +151,7 @@ BKSWRET10: POP AF ; G
ORG 0EFF8h ORG 0EFF8h
DB 0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh DB 0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh
ENDIF ENDIF
; SFD700 - Pad to 10000H ; SFD700 - Pad to 10000H
IF BUILD_SFD700 = 1 IF BUILD_SFD700 = 1
ALIGN 10000H ALIGN 10000H

View File

@@ -37,6 +37,13 @@
;- along with this program. If not, see <http://www.gnu.org/licenses/>. ;- along with this program. If not, see <http://www.gnu.org/licenses/>.
;-------------------------------------------------------------------------------------------------------- ;--------------------------------------------------------------------------------------------------------
IF BUILD_SFD700 = 1
ORG 0E000H
ALIGN 0E300H
DB "BANK11"
ALIGN UROMADDR
ENDIF
;=========================================================== ;===========================================================
; ;
; USER ROM BANK 11 - ; USER ROM BANK 11 -
@@ -382,7 +389,10 @@ HELPSCR: IF BUILD_ROMDISK+BUILD_PICOZ80 = 1
DB "DXXXX[YYYY] - dump mem X to Y.", 00DH DB "DXXXX[YYYY] - dump mem X to Y.", 00DH
DB "DASMXXXX[YYYY]", 00DH DB "DASMXXXX[YYYY]", 00DH
DB " disassemble X to Y", 00DH DB " disassemble X to Y", 00DH
DB "FC[XXXXYYYYZZZZ] - save mem to floppy.", 00DH
DB " X=start,Y=end,Z=exec", 00DH
DB "FD/FL - fd dir/boot", 00DH DB "FD/FL - fd dir/boot", 00DH
DB "FD2T - copy floppy to tape.", 00DH
DB "H - this help screen.", 00DH DB "H - this help screen.", 00DH
DB "IR - rfs rom dir listing.", 00DH DB "IR - rfs rom dir listing.", 00DH
DB "JXXXX - jump to location X.", 00DH DB "JXXXX - jump to location X.", 00DH
@@ -391,14 +401,15 @@ HELPSCR: IF BUILD_ROMDISK+BUILD_PICOZ80 = 1
DB " - add NX for no exec, ie.LRNX.", 00DH DB " - add NX for no exec, ie.LRNX.", 00DH
DB "MXXXX - edit memory starting at X.", 00DH DB "MXXXX - edit memory starting at X.", 00DH
DB "P - test printer.", 00DH DB "P - test printer.", 00DH
DB "QD/QL - QD dir/boot", 00DH ;DB "QD/QL - QD dir/boot", 00DH
DB "R - test dram memory.", 00DH DB "R - test dram memory.", 00DH
DB "SD2T - copy sd card to tape.", 00DH ;DB "SD2T - copy sd card to tape.", 00DH
DB "ST[XXXXYYYYZZZZ] - save mem to tape.", 00DH DB "ST[XXXXYYYYZZZZ] - save mem to tape.", 00DH
DB "SC[XXXXYYYYZZZZ] - save mem to card.", 00DH ;DB "SC[XXXXYYYYZZZZ] - save mem to card.", 00DH
DB " X=start,Y=end,Z=exec", 00DH DB " X=start,Y=end,Z=exec", 00DH
DB "T - test timer.", 00DH DB "T - test timer.", 00DH
DB "T2SD - copy tape to sd card.", 00DH ;DB "T2SD - copy tape to sd card.", 00DH
DB "T2FD - copy tape to floppy.", 00DH
DB "V - verify tape save.", 00DH DB "V - verify tape save.", 00DH
DB 000H DB 000H
ENDIF ENDIF

View File

@@ -218,7 +218,28 @@ TDELAYB1: RRA
;------------------------------------------------------------------------------- ;-------------------------------------------------------------------------------
; START OF SD CONTROLLER FUNCTIONALITY ; START OF SD CONTROLLER FUNCTIONALITY
; SD card hardware not present on SFD700 — exclude for that build.
;------------------------------------------------------------------------------- ;-------------------------------------------------------------------------------
; Dummy EQUs for SD functions — not used on SFD700 (no SD hardware).
; Bank 0 CMT intercept handlers reference these. Must be outside IF for Glass visibility.
SDINIT EQU BUILD_SFD700 * 0
LOADSDCARD EQU BUILD_SFD700 * 0
LOADSDCARDX EQU BUILD_SFD700 * 0
LOADSDCP EQU BUILD_SFD700 * 0
LOADSDINF EQU BUILD_SFD700 * 0
LOADSDDATA EQU BUILD_SFD700 * 0
LOADSD3 EQU BUILD_SFD700 * 0
SAVESDCARD EQU BUILD_SFD700 * 0
SAVESDCARDX EQU BUILD_SFD700 * 0
SAVESDDATA EQU BUILD_SFD700 * 0
DIRSDCARD EQU BUILD_SFD700 * 0
FINDSDX EQU BUILD_SFD700 * 0
SDPRINT EQU BUILD_SFD700 * 0
LOADSD9BC EQU BUILD_SFD700 * 0
LOADSD9 EQU BUILD_SFD700 * 0
LOADSD11 EQU BUILD_SFD700 * 0
IF BUILD_ROMDISK+BUILD_PICOZ80 = 1
;------------------------------------------------------------------------------- ;-------------------------------------------------------------------------------
; Hardware SPI SD Controller (HW_SPI_ENA = 1) ; Hardware SPI SD Controller (HW_SPI_ENA = 1)
@@ -1612,6 +1633,7 @@ LOADSD9BC: LD H,B
;------------------------------------------------------------------------------- ;-------------------------------------------------------------------------------
; END OF SD CONTROLLER FUNCTIONALITY ; END OF SD CONTROLLER FUNCTIONALITY
;------------------------------------------------------------------------------- ;-------------------------------------------------------------------------------
ENDIF ; BUILD_ROMDISK+BUILD_PICOZ80 (SD controller)
; RomDisk, top 8 bytes are used by the control registers when enabled so dont use the space. ; RomDisk, top 8 bytes are used by the control registers when enabled so dont use the space.
IF BUILD_ROMDISK+BUILD_PICOZ80 = 1 IF BUILD_ROMDISK+BUILD_PICOZ80 = 1

View File

@@ -143,21 +143,16 @@ BKSWRET3: POP AF ; G
RET RET
;------------------------------------------------------------------------------- ;-------------------------------------------------------------------------------
; START OF TAPE/SD CMDLINE TOOLS FUNCTIONALITY ; START OF TAPE/SD/FD CMDLINE TOOLS FUNCTIONALITY
;------------------------------------------------------------------------------- ;-------------------------------------------------------------------------------
; Method to copy an application on a tape to an SD stored application. The tape drive is read and the first IF BUILD_ROMDISK+BUILD_PICOZ80 = 1
; encountered program is loaded into memory at 0x1200. The CMT header is populated with the correct details (even if ; Method to copy an application on a tape to an SD stored application.
; the load address isnt 0x1200, the CMT Header contains the correct value). TAPE2SD: LD HL,LOADTAPECP
; A call is then made to write the application to the SD card.
;
TAPE2SD: ; Load from tape into memory, filling the tape CMT header and loading data into location 0x1200.
LD HL,LOADTAPECP ; Call the Loadtape command, non execute version to get the tape contents into memory.
CALL BKSW3to4 CALL BKSW3to4
LD A,(RESULT) LD A,(RESULT)
OR A OR A
JR NZ,TAPE2SDERR JR NZ,TAPE2SDERR
; Save to SD Card.
LD HL,SAVESDCARDX LD HL,SAVESDCARDX
CALL BKSW3to2 CALL BKSW3to2
LD A,(RESULT) LD A,(RESULT)
@@ -171,12 +166,7 @@ TAPE2SDERR2:LD HL,PRINTMSG
RET RET
; Method to copy an SD stored application to a Cassette tape in the CMT. ; Method to copy an SD stored application to a Cassette tape in the CMT.
; The directory entry number or filename is passed to the command and the entry is located within the SD SD2TAPE: LD HL,LOADSDCP
; directory structure. The file is then loaded into memory and the CMT header populated. A call is then made
; to write out the data to tap.
;
SD2TAPE: ; Load from SD, fill the CMT header then call CMT save.
LD HL,LOADSDCP
CALL BKSW3to2 CALL BKSW3to2
LD A,(RESULT) LD A,(RESULT)
OR A OR A
@@ -190,8 +180,97 @@ SD2TAPE: ; Load from SD, fill the CMT header then call CMT save.
SD2TAPEERR: LDDE MSGSD2TERR SD2TAPEERR: LDDE MSGSD2TERR
JR TAPE2SDERR2 JR TAPE2SDERR2
RET RET
ENDIF
IF BUILD_SFD700 = 1
;------------------------------------------------------------------------------- ;-------------------------------------------------------------------------------
; END OF TAPE/SD CMDLINE TOOLS FUNCTIONALITY ; FD2TAPE - Copy floppy disk file to cassette tape.
; Reads boot sector + file from floppy, populates CMT header, writes to tape.
;-------------------------------------------------------------------------------
FD2TAPE: ; Prompt for filename to extract from floppy.
CALL NL
LDDE MSGFDFNAME
CALL MSG ; "FILENAME? "
LD DE,BUFER
CALL GETL ; Get user input.
LD HL,BUFER+10 ; Skip past prompt echo.
LD DE,NAME
LD BC,FNSIZE
LDIR ; Copy filename to NAME.
;
LD HL,LOADFDCP ; Load named file from floppy (bank 1).
CALL BKSW3to1
LD A,(RESULT)
OR A
JR Z,FD2T_SAV ; Success → save to tape.
CP 0FEH
RET Z ; 0xFE = FD7ERR already showed error.
JR FD2TERR ; 0xFF = show our error message.
FD2T_SAV: LD HL,SAVECMT ; Write to tape (bank 4).
CALL BKSW3to4
LD A,(RESULT)
OR A
JR NZ,FD2TERR
LDDE MSGFD2TOK
JR FDTMSG
FD2TERR: LDDE MSGFD2TERR
; Print local message: DE = string in THIS bank (CR-terminated).
; Uses monitor ROM MSG function directly (no bank switch needed).
FDTMSG: CALL NL
CALL MSG ; Print CR-terminated string at DE.
RET
;-------------------------------------------------------------------------------
; TAPE2FD - Copy cassette tape file to floppy disk.
; Loads from tape, then writes IPL header + data to floppy.
;-------------------------------------------------------------------------------
TAPE2FD: LD HL,LOADTAPECP ; Load from tape (bank 4).
CALL BKSW3to4
LD A,(RESULT)
OR A
JR NZ,T2FDERR
LD HL,SAVEFDX ; Write to floppy (bank 1).
CALL BKSW3to1
LD A,(RESULT)
OR A
JR NZ,T2FDERR
LDDE MSGT2FDOK
JR FDTMSG
T2FDERR: LDDE MSGT2FDERR
JR FDTMSG
;-------------------------------------------------------------------------------
; SAVEFDCARD - Save memory region to floppy disk (FC command).
; Gets CMT parameters (name, start, size, exec) then writes to floppy.
;-------------------------------------------------------------------------------
SAVEFDCARD: CALL GETCMTPARM ; Get parameters (same bank).
LD A,C
OR A
JR NZ,SFDC_ERR ; C=1 means input error.
LD A,001H ; Default file type = OBJ.
LD (ATRB),A
LD HL,SAVEFDX ; Write to floppy (bank 1).
CALL BKSW3to1
LD A,(RESULT)
OR A
JR NZ,SFDC_ERR
LDDE MSGFCSAVEOK
JR FDTMSG
SFDC_ERR: LDDE MSGFCSAVEERR
JR FDTMSG
; Messages for floppy/tape transfer commands (local to bank 3).
MSGFDFNAME: DB "FILENAME? ", 00DH
MSGFD2TOK: DB "FLOPPY -> TAPE OK.", 00DH
MSGFD2TERR: DB "FLOPPY -> TAPE ERROR.", 00DH
MSGT2FDOK: DB "TAPE -> FLOPPY OK.", 00DH
MSGT2FDERR: DB "TAPE -> FLOPPY ERROR.", 00DH
MSGFCSAVEOK:DB "SAVED TO FLOPPY OK.", 00DH
MSGFCSAVEERR:DB "SAVE TO FLOPPY ERROR.", 00DH
ENDIF
;-------------------------------------------------------------------------------
; END OF TAPE/SD/FD CMDLINE TOOLS FUNCTIONALITY
;------------------------------------------------------------------------------- ;-------------------------------------------------------------------------------
@@ -402,7 +481,7 @@ GETCMTPARM: CALL READ4HEX ; Start
CALL NL CALL NL
LDDE MSGSAVE ; 'FILENAME? ' LDDE MSGSAVE ; 'FILENAME? '
LD HL,PRINTMSG LD HL,PRINTMSG
CALL BKSW2to6 ; Print out the filename. CALL BKSW3to6 ; Print out the filename prompt.
LD DE,BUFER LD DE,BUFER
CALL GETL CALL GETL
LD HL,BUFER+10 LD HL,BUFER+10

View File

@@ -149,12 +149,12 @@ DASM_FILL: LD (HL),A
CALL DASMBITINST ; Is This a BIT,SET or RES Inst.? CALL DASMBITINST ; Is This a BIT,SET or RES Inst.?
JP NZ,DASM_FIND ; NO=Do regular search JP NZ,DASM_FIND ; NO=Do regular search
CALL DSMFINDOPCD ; Search For Matching BIT,SET,RES OP Code CALL DSMFINDOPCD ; Search For Matching BIT,SET,RES OP Code
JP Z,DASM_ERR ; NOT FOUND - Error Message JP Z,DASM_UNK ; NOT FOUND - output raw byte and continue
CALL DASMGETINST ; Copy 4 byte Assembler Inst. to ASM_BUF CALL DASMGETINST ; Copy 4 byte Assembler Inst. to ASM_BUF
JP DASM_JUSTV ; VAL_BUF has already been populated JP DASM_JUSTV ; VAL_BUF has already been populated
DASM_FIND: CALL DSMFINDOPCD ; Search For Matching OP Code DASM_FIND: CALL DSMFINDOPCD ; Search For Matching OP Code
JP Z,DASM_ERR ; NOT FOUND - Error Message JP Z,DASM_UNK ; NOT FOUND - output raw byte and continue
CALL DASMGETINST ; Copy 4 byte Assembler Inst. to ASM_BUF CALL DASMGETINST ; Copy 4 byte Assembler Inst. to ASM_BUF
@@ -173,7 +173,7 @@ DASM_JUSTV: CALL DSMLFJVAL ; Left justify VAL_BUF(Trim leading zeros)
CALL DSMRTRIMASM ; Trim trailing spaces from ASM_BUF CALL DSMRTRIMASM ; Trim trailing spaces from ASM_BUF
LD A,0Fh ; Output Memory Bytes and Advance Address Pointer DASM_PROUT: LD A,0Fh ; Output Memory Bytes and Advance Address Pointer
LD C,A LD C,A
LD HL,(ADDR_LO) ; Address ->HL LD HL,(ADDR_LO) ; Address ->HL
LD A,(ML_BTCOUNT) ;Get Total Byte count from lookup table LD A,(ML_BTCOUNT) ;Get Total Byte count from lookup table
@@ -197,6 +197,8 @@ DASM_SPCOUT: CALL PRNTS ; Pad with spaces to 15 bytes
EX DE,HL EX DE,HL
LD HL,PRINTMSG LD HL,PRINTMSG
CALL BKSW7to6 CALL BKSW7to6
LD A,ROMBANK7+1 ; Restore F-page (BNKSWSELRET sets it to ROMBK1).
OUT (REG_FXXX),A
LD A,(COUNT_C) ; Get Row Count LD A,(COUNT_C) ; Get Row Count
CP 0FFh ; Using End Address? CP 0FFh ; Using End Address?
@@ -212,9 +214,40 @@ DASM_CKEADR: LD HL,(ASM_ADDR) ; Get End Address
RET M ; If resulte negative - Exit RET M ; If resulte negative - Exit
JP DASM_LOOP1 ; Otherwise Continue... JP DASM_LOOP1 ; Otherwise Continue...
; Unknown/undefined opcode — output as "DB" and advance 1 byte.
DASM_UNK: LD A,1
LD (ML_BTCOUNT),A ; 1 byte only
LD HL,DASM_UNKDB ; "DB" string → ASM_BUF
LD DE,ASM_BUF
LD A,(HL)
LD (DE),A
INC HL
INC DE
LD A,(HL)
LD (DE),A
INC DE
XOR A
LD (DE),A ; Null-terminate "DB"
; Put hex value of opcode into VAL_BUF
LD A,(ML_BUF)
CALL BYTE2ASCII ; H=LSB nib, L=MSB nib
LD A,L
LD (VAL_BUF),A
LD A,H
LD (VAL_BUF+1),A
LD A,'H'
LD (VAL_BUF+2),A
XOR A
LD (VAL_BUF+3),A ; Null-terminate
JP DASM_PROUT ; Go to print output
DASM_UNKDB: DB "DB", 0
DASM_ERR: LD DE,MSGNOTFND DASM_ERR: LD DE,MSGNOTFND
LD HL,PRINTMSG LD HL,PRINTMSG
CALL BKSW7to6 CALL BKSW7to6
LD A,ROMBANK7+1 ; Restore F-page.
OUT (REG_FXXX),A
RET RET
;****************************************************************** ;******************************************************************
@@ -560,7 +593,8 @@ DASM_MOVINS: INC HL ; Inc Block Header data pointer
; Return with BLK_ADDR=block header address, ROW_ADDR=found row address ; Return with BLK_ADDR=block header address, ROW_ADDR=found row address
; ML_BYCOUNT=a non-zero count if match was found ; ML_BYCOUNT=a non-zero count if match was found
;****************************************************************** ;******************************************************************
DSMFINDOPCD: PUSH BC DSMFINDOPCD: DI ;Disable interrupts during F-page table search.
PUSH BC
LD IX,OPCD_TABLE ;Set IX=Table Start Address LD IX,OPCD_TABLE ;Set IX=Table Start Address
DSMFN_LOOP1: LD A,(IX+0) DSMFN_LOOP1: LD A,(IX+0)
CP '#' ;Is Row Byte0='#'? CP '#' ;Is Row Byte0='#'?
@@ -607,6 +641,7 @@ DSMFN_MATCH: LD (ROW_ADDR),IX ; Save IX->ROW_ADDR
LD (ML_BTCOUNT),A ; Save byte count->ML_BYCOUNT LD (ML_BTCOUNT),A ; Save byte count->ML_BYCOUNT
CP 0 ; Set Z flag if NOT FOUND CP 0 ; Set Z flag if NOT FOUND
POP BC POP BC
EI ;Re-enable interrupts after table search.
RET RET
;****************************************************************** ;******************************************************************
@@ -728,6 +763,8 @@ ASMLOOPEXIT: LD BC,VAL_BUF
ASM_ERR_INS: LD DE,MSGNOINSTR ASM_ERR_INS: LD DE,MSGNOINSTR
LD HL,PRINTMSG LD HL,PRINTMSG
CALL BKSW7to6 CALL BKSW7to6
LD A,ROMBANK7+1 ; Restore F-page.
OUT (REG_FXXX),A
JP ASM_LOOP1 ; Get another line of ASM input... JP ASM_LOOP1 ; Get another line of ASM input...
ASM_FINDPRM: CALL ASMFINDPARM ; Look for matching parameter pattern ASM_FINDPRM: CALL ASMFINDPARM ; Look for matching parameter pattern
@@ -737,6 +774,8 @@ ASM_FINDPRM: CALL ASMFINDPARM ; Look for matching parameter pattern
LD DE,MSGNOPARAM LD DE,MSGNOPARAM
LD HL,PRINTMSG LD HL,PRINTMSG
CALL BKSW7to6 CALL BKSW7to6
LD A,ROMBANK7+1 ; Restore F-page.
OUT (REG_FXXX),A
JP ASM_LOOP1 ; Get another line of ASM input... JP ASM_LOOP1 ; Get another line of ASM input...
@@ -988,12 +1027,12 @@ DASM_FILL: LD (HL),A
; BIT/SET/RES path: value already in VAL_BUF ; BIT/SET/RES path: value already in VAL_BUF
CALL DSMFIND_BOTH ; Search both MROM banks CALL DSMFIND_BOTH ; Search both MROM banks
JP Z,DASM_ERR2 ; NOT FOUND JP Z,DASM_UNK2 ; NOT FOUND - output raw byte and continue
CALL DASMGETINST ; Copy instruction name (MROM still active) CALL DASMGETINST ; Copy instruction name (MROM still active)
JP DASM_JUSTV ; Skip to value justification JP DASM_JUSTV ; Skip to value justification
DASM_FIND: CALL DSMFIND_BOTH ; Search both MROM banks DASM_FIND: CALL DSMFIND_BOTH ; Search both MROM banks
JP Z,DASM_ERR2 ; NOT FOUND JP Z,DASM_UNK2 ; NOT FOUND - output raw byte and continue
CALL DASMGETINST ; Copy instruction name CALL DASMGETINST ; Copy instruction name
; Set up VAL_BUF defaults ; Set up VAL_BUF defaults
@@ -1014,7 +1053,7 @@ DASM_JUSTV: CALL DSMLFJVAL ; Left justify VAL_BUF
CALL DSMRTRIMASM ; Trim trailing spaces CALL DSMRTRIMASM ; Trim trailing spaces
; Output memory bytes and advance address pointer DASM_PROUT2: ; Output memory bytes and advance address pointer
LD A,0Fh LD A,0Fh
LD C,A LD C,A
LD HL,(ADDR_LO) LD HL,(ADDR_LO)
@@ -1054,6 +1093,36 @@ DASM_CKEADR: LD HL,(ASM_ADDR)
RET M RET M
JP DASM_LOOP1 JP DASM_LOOP1
; Unknown/undefined opcode (picoZ80) — output as "DB" and advance 1 byte.
DASM_UNK2: POP AF ; Restore MROM bank
LD (BNKSELMROM),A
LD A,1
LD (ML_BTCOUNT),A ; 1 byte only
LD HL,DASM_UNKDB2 ; "DB" string → ASM_BUF
LD DE,ASM_BUF
LD A,(HL)
LD (DE),A
INC HL
INC DE
LD A,(HL)
LD (DE),A
INC DE
XOR A
LD (DE),A ; Null-terminate "DB"
LD A,(ML_BUF)
CALL BYTE2ASCII ; H=LSB nib, L=MSB nib
LD A,L
LD (VAL_BUF),A
LD A,H
LD (VAL_BUF+1),A
LD A,'H'
LD (VAL_BUF+2),A
XOR A
LD (VAL_BUF+3),A
JP DASM_PROUT2 ; Go to print output
DASM_UNKDB2: DB "DB", 0
DASM_ERR2: POP AF ; Restore MROM bank DASM_ERR2: POP AF ; Restore MROM bank
LD (BNKSELMROM),A LD (BNKSELMROM),A
DASM_ERR: LD DE,MSGNOTFND DASM_ERR: LD DE,MSGNOTFND

View File

@@ -36,6 +36,13 @@
;- along with this program. If not, see <http://www.gnu.org/licenses/>. ;- along with this program. If not, see <http://www.gnu.org/licenses/>.
;-------------------------------------------------------------------------------------------------------- ;--------------------------------------------------------------------------------------------------------
IF BUILD_SFD700 = 1
ORG 0E000H
ALIGN 0E300H
DB "BANK8"
ALIGN UROMADDR
ENDIF
;=========================================================== ;===========================================================
; ;
; USER ROM BANK 8 - ; USER ROM BANK 8 -
@@ -579,13 +586,355 @@ CPY2SP_EX8: RET
; END OF METHODS ; END OF METHODS
;------------------------------------------------------------------------------- ;-------------------------------------------------------------------------------
;===============================================================================
; MZ-80A FLOPPY DISK CONTROLLER (SFD700) — Bank 8 (8K: E300-FFFF).
;
; Hardware-accelerated read using A10 address toggle:
; F3FE: DD E9 = JP (IX) — spin loop (A10=0, DRQ low).
; F7FE: FD E9 = JP (IY) — data handler (A10=1, DRQ high).
; These are the ORIGINAL MZ-80A FI ROM addresses that BASIC expects.
;
; Bank 8 = ROMBANK8 = 12 (>= ROMBANK6), so E-page + F-page are both mapped.
;===============================================================================
IF BUILD_SFD700 = 1
; MZ-80A specific data.
DSKID80A: DB 002H, "IPLPRO"
PRMBLK80A: DB 000H, 000H, 000H, 000H, 001H, 000H, 0CEH, 000H, 000H, 000H, 000H
ERRTONE8: DB "A0", 0D7H, "ARA", 0D7H, "AR", 00DH
FD80A_TSP EQU 0101EH ; In RAM, not ROM.
ENDIF
; Glass: label outside IF for global visibility.
FD80A_BOOT:
IF BUILD_SFD700 = 1
PUSH DE
LD DE,BPARA
LD HL,PRMBLK80A
LD BC,11
LDIR
POP DE
LD A,(DE)
CP 00DH
JR NZ,FD80A_GDSK
CALL FD80A_INIT
FD80A_PRM: LDDE MSGBOOTDRV
LD HL,PRINTMSG
CALL BKSW8to6
LD DE,011A3H
CALL GETL
LD A,(DE)
CP 01BH
JP Z,FD80A_BRK
LD HL,19
ADD HL,DE
LD A,(HL)
CP 00DH
JR Z,FD80A_RD1
FD80A_GDSK: CALL HEX
JR C,FD80A_PRM
DEC A
CP 004H
JR NC,FD80A_PRM
LD (BPARA),A
FD80A_RD1: LD IX,BPARA
CALL FD80A_READ
LD HL,0CE00H
LD DE,DSKID80A
LD B,007H
FD80A_CHK: LD C,(HL)
LD A,(DE)
CP C
JP NZ,FD80A_NMS
INC HL
INC DE
DJNZ FD80A_CHK
LDDE MSGIPLLOAD
LD HL,PRINTMSG
CALL BKSW8to6
LD DE,0CE07H
LD HL,PRTFN
CALL BKSW8to6
LD IX,BPARA
LD HL,(0CE16H)
LD (IX+5),L
LD (IX+6),H
LD HL,(0CE14H)
LD (IX+3),L
LD (IX+4),H
LD HL,(0CE1EH)
LD (IX+1),L
LD (IX+2),H
CALL FD80A_READ
CALL FD80A_INIT
LD HL,(0CE18H)
JP (HL)
FD80A_LERR: LDDE MSGLOADERR
JR FD80A_ERR
FD80A_NMS: LDDE MSGDSKNOTMST
FD80A_ERR: LD HL,(TMPSTACKP)
LD (FD80A_TSP),HL
LD HL,PRINTMSG
CALL BKSW8to6
LD DE,ERRTONE8
CALL MELDY
LD HL,(FD80A_TSP)
LD SP,HL
RET
FD80A_BRK: LD SP,(TMPSTACKP)
RET
; --- Low-level FDC routines ---
FD80A_RDY: LD A,(MOTON)
RRCA
CALL NC,FD80A_MON
LD A,(IX+0)
OR 084H
OUT (FDC_DRIVE),A
XOR A
LD (FDCCMD),A
LD HL,0
FD80A_RDW: DEC HL
LD A,H
OR L
JP Z,FD80A_DSKE
IN A,(FDC_CMD)
CPL
RLCA
JR C,FD80A_RDW
LD C,(IX+0)
LD HL,TRK0FD1
LD B,0
ADD HL,BC
BIT 0,(HL)
RET NZ
CALL FD80A_SK0
SET 0,(HL)
RET
FD80A_MON: LD A,080H
OUT (FDC_DRIVE),A
LD B,16
FD80A_MNW: CALL FD80A_DL60
DJNZ FD80A_MNW
LD A,1
LD (MOTON),A
RET
FD80A_SEEK: LD A,01BH
CALL FD80A_CMD
AND 099H
RET
FD80A_INIT: XOR A
OUT (FDC_DRIVE),A
LD (TRK0FD1),A
LD (TRK0FD2),A
LD (TRK0FD3),A
LD (TRK0FD4),A
LD (MOTON),A
RET
FD80A_SK0: LD A,00BH
CALL FD80A_CMD
AND 085H
XOR 004H
RET Z
JP FD80A_DSKE
FD80A_CMD: LD (FDCCMD),A
CPL
OUT (FDC_CMD),A
CALL FD80A_WBR
IN A,(FDC_CMD)
CPL
RET
FD80A_WBR: PUSH DE
PUSH HL
CALL FD80A_DL7
LD E,7
FD80A_WB1: LD HL,0
FD80A_WB2: DEC HL
LD A,H
OR L
JR Z,FD80A_WB3
IN A,(FDC_CMD)
CPL
RRCA
JR C,FD80A_WB2
POP HL
POP DE
RET
FD80A_WB3: DEC E
JR NZ,FD80A_WB1
POP HL
POP DE
JP FD80A_DSKE
FD80A_WBA: PUSH DE
PUSH HL
CALL FD80A_DL7
LD E,7
FD80A_WA1: LD HL,0
FD80A_WA2: DEC HL
LD A,H
OR L
JR Z,FD80A_WA3
IN A,(FDC_CMD)
CPL
RRCA
JR NC,FD80A_WA2
POP HL
POP DE
RET
FD80A_WA3: DEC E
JR NZ,FD80A_WA1
POP HL
POP DE
JP FD80A_DSKE
; --- Hardware-accelerated sector read ---
; Uses A10 toggle: F3FE=JP(IX) spin, F7FE=JP(IY) data.
FD80A_READ: CALL FD80A_CNV
FD80A_R1: CALL FD80A_PRS
FD80A_R2: CALL FD80A_SID
CALL FD80A_SEEK
JR NZ,FD80A_RET
CALL FD80A_STR
PUSH IX
LD IX,0F3FEH ; Spin loop at F3FE (original FI ROM addr).
LD IY,FD80A_INI ; Data handler.
LD A,094H
CALL FD80A_CM2
FD80A_R3: LD B,0
JP (IX) ; Enter spin loop.
FD80A_INI: INI
JP NZ,0F3FEH ; Back to spin loop.
POP IX
INC (IX+8)
LD A,(IX+8)
PUSH IX
LD IX,0F3FEH
CP 17
JR Z,FD80A_SEC
DEC D
JR NZ,FD80A_R3
JR FD80A_RDN
FD80A_SEC: DEC D
FD80A_RDN: CALL FD80A_FI
POP IX
IN A,(FDC_CMD)
CPL
AND 0FFH
JR NZ,FD80A_RET
CALL FD80A_ADJ
JP Z,FD80A_END
LD A,(IX+7)
JR FD80A_R2
FD80A_RET: LD A,(RETRIES)
DEC A
LD (RETRIES),A
JP Z,FD80A_DSKE
CALL FD80A_SK0
JR FD80A_R1
FD80A_END: LD A,080H
OUT (FDC_DRIVE),A
RET
; --- Helpers ---
FD80A_PRS: CALL FD80A_RDY
LD D,(IX+4)
LD A,(IX+3)
OR A
JR Z,FD80A_PS1
INC D
FD80A_PS1: LD A,(IX+10)
LD (IX+8),A
LD A,(IX+9)
LD (IX+7),A
LD L,(IX+5)
LD H,(IX+6)
RET
FD80A_SID: SRL A
CPL
OUT (FDC_DATA),A
JR NC,FD80A_S0
LD A,1
JR FD80A_S1
FD80A_S0: XOR A
FD80A_S1: CPL
OUT (FDC_SIDE),A
RET
FD80A_STR: LD C,FDC_DATA
LD A,(IX+7)
SRL A
CPL
OUT (FDC_TRACK),A
LD A,(IX+8)
CPL
OUT (FDC_SECTOR),A
RET
FD80A_ADJ: LD A,(IX+8)
CP 17
JR NZ,FD80A_AJ1
LD A,1
LD (IX+8),A
INC (IX+7)
FD80A_AJ1: LD A,D
OR A
RET
FD80A_CNV: LD B,0
LD DE,16
LD L,(IX+1)
LD H,(IX+2)
XOR A
FD80A_CN1: SBC HL,DE
JR C,FD80A_CN2
INC B
JR FD80A_CN1
FD80A_CN2: ADD HL,DE
LD H,B
INC L
LD (IX+9),H
LD (IX+10),L
LD A,10
LD (RETRIES),A
RET
FD80A_CM2: LD (FDCCMD),A
CPL
OUT (FDC_CMD),A
CALL FD80A_WBA
RET
FD80A_FI: LD A,0D8H
CPL
OUT (FDC_CMD),A
CALL FD80A_WBR
RET
FD80A_DSKE: CALL FD80A_INIT
JP FD80A_LERR
FD80A_DL7: PUSH DE
LD DE,7
JR FD80A_DLP
FD80A_DL60: PUSH DE
LD DE,01013H
FD80A_DLP: DEC DE
LD A,E
OR D
JR NZ,FD80A_DLP
POP DE
RET
; --- Hardware acceleration alignment points (F-page) ---
; JP (IX) at F3FE: A10=0, DRQ low spin loop.
ALIGN_NOPS 0F3FEH
JP (IX) ; DD E9 at F3FE/F3FF.
; JP (IY) at F7FE: A10=1, DRQ high data handler.
ALIGN_NOPS 0F7FEH
JP (IY) ; FD E9 at F7FE/F7FF.
; SFD700 - Pad to end of 8K bank (F-page through FFFFH).
ALIGN 10000H
ENDIF ; BUILD_SFD700
; RomDisk - Pad to EFFF boundary. ; RomDisk - Pad to EFFF boundary.
IF BUILD_ROMDISK+BUILD_PICOZ80 = 1 IF BUILD_ROMDISK+BUILD_PICOZ80 = 1
ALIGN 0EFF8h ALIGN 0EFF8h
ORG 0EFF8h ORG 0EFF8h
DB 0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh DB 0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh
ENDIF ENDIF
; SFD700 - Pad to 10000H
IF BUILD_SFD700 = 1
ALIGN 10000H
ENDIF

View File

@@ -38,6 +38,13 @@
;- along with this program. If not, see <http://www.gnu.org/licenses/>. ;- along with this program. If not, see <http://www.gnu.org/licenses/>.
;-------------------------------------------------------------------------------------------------------- ;--------------------------------------------------------------------------------------------------------
IF BUILD_SFD700 = 1
ORG 0E000H
ALIGN 0E300H
DB "BANK9"
ALIGN UROMADDR
ENDIF
;=========================================================== ;===========================================================
; ;
; USER ROM BANK 9 - ; USER ROM BANK 9 -

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

BIN
roms/rfs.rom vendored

Binary file not shown.

BIN
tools/MZFD/MZFDTool vendored Executable file

Binary file not shown.

618
tools/MZFD/MZFDTool.c Executable file
View File

@@ -0,0 +1,618 @@
/* =========================================================================
* MZFDTool — Sharp MZ-700 Floppy Disk image tool
*
* Creates and manages raw floppy disk images for the MZ-700 FDC system.
* Supports format, directory listing, adding/extracting MZF files, and
* setting a boot program.
*
* Original V1.01 (c) 2002 by BKK (DOS BIOS int13h version)
* Updated V2.00 (c) 2026 Philip Smart — rewritten for Linux with raw
* image file I/O, proper directory management, MZF conversion,
* and robust error handling.
*
* MZ-700 Floppy Disk format (327,680 bytes raw image):
*
* Geometry: 40 cylinders, 2 heads, 16 sectors/track, 256 bytes/sector.
* Data on disk is INVERTED (XOR 0xFF) due to MZ-700 hardware.
*
* Logical sector = (cylinder * 2 + head) * 16 + (phys_sector - 1)
* Total sectors: 40 * 2 * 16 = 1280
*
* Boot sector: Logical sector 0 (Cyl 0, Head 0, Sector 1)
* Directory: Logical sectors 16-31 (Cyl 0, Head 1, Sectors 1-16)
* 16 sectors * 8 entries/sector = 128 entries
* Entry 0 of sector 16 = volume/FAT header
* Data area: Logical sector 32+ (Cyl 1+)
*
* Boot sector (32 bytes used):
* +00: Type (03 = MZ-700 bootable)
* +01: Signature "IPLPRO" (6 bytes)
* +07: Program name (11 bytes, CR-terminated)
* +12: Load address (2 bytes LE)
* +14: File size (2 bytes LE)
* +16: Reserved (8 bytes)
* +1E: Start data sector (2 bytes LE)
*
* Directory entry (32 bytes):
* +00: FileType (01=OBJ, 02=BTX, 00=deleted, >=80h=header)
* +01: FileName (17 bytes, CR-terminated, space-padded)
* +12: LockFlag (1 byte)
* +13: DummyFlag (1 byte)
* +14: FileSize (2 bytes LE, in bytes)
* +16: LoadAddress (2 bytes LE)
* +18: ExecAddress (2 bytes LE)
* +1A: Reserved (4 bytes)
* +1E: SectorAddress (2 bytes LE, logical)
*
* Usage:
* MZFDTool format [-o disk.img] Format empty disk image
* MZFDTool dir [-o disk.img] List directory
* MZFDTool add <file.mzf> [-o disk.img] Add MZF file to directory
* MZFDTool extract <name> [-o disk.img] Extract file to MZF
* MZFDTool boot <file.mzf> [-o disk.img] Set boot program
* ========================================================================= */
#define VERSION "2.00"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <ctype.h>
#define CYLS 40
#define HEADS 2
#define SECTORS 16
#define SECSIZE 256
#define TOTAL_SECTORS (CYLS * HEADS * SECTORS)
#define IMGSIZE (TOTAL_SECTORS * SECSIZE)
#define DIR_FIRST_SEC 16 /* First directory logical sector */
#define DIR_SECTORS 16 /* Number of directory sectors */
#define DIR_ENTRIES 8 /* Entries per sector */
#define DATA_FIRST_SEC 32 /* First data logical sector */
#define MAXFILETYPES 12
#define DEFAULTIMAGE "MZ700.img"
#define CMTHDRSIZE 128 /* MZF/CMT file header size */
#define BOOT_SIG_TYPE 3 /* MZ-700 boot type */
#define BOOT_SIG "IPLPRO"
#define BOOT_SIG_LEN 6
#pragma pack(push, 1)
/* Boot sector structure (first 32 bytes of logical sector 0) */
struct BootSector {
uint8_t type; /* +00: 03 = MZ-700 bootable */
char signature[6]; /* +01: "IPLPRO" */
char name[11]; /* +07: Program name (CR-terminated) */
uint16_t loadAddress; /* +12: Load address */
uint16_t fileSize; /* +14: File size in bytes */
uint8_t reserved[8]; /* +16: Reserved */
uint16_t sectorAddress; /* +1E: Start data sector */
};
/* Directory entry (32 bytes) */
struct DirEntry {
uint8_t fileType; /* +00: 01=OBJ, 02=BTX, 00=deleted */
char fileName[17]; /* +01: CR-terminated, space-padded */
uint8_t lockFlag; /* +12: Lock flag */
uint8_t dummyFlag; /* +13: Reserved */
uint16_t fileSize; /* +14: File size in bytes */
uint16_t loadAddress; /* +16: Load address */
uint16_t execAddress; /* +18: Exec address */
uint8_t dummy[4]; /* +1A: Reserved */
uint16_t sectorAddress; /* +1E: Start sector (logical) */
};
/* MZF/CMT tape file header (128 bytes) */
struct CMTHeader {
uint8_t attribute; /* File type: 01=OBJ, 02=BTX, etc. */
char name[17]; /* Filename (CR-terminated) */
uint16_t size; /* Data size */
uint16_t loadAddress; /* Load address */
uint16_t execAddress; /* Execution address */
char comment[104]; /* Comment area */
};
#pragma pack(pop)
static const char *FileTypes[MAXFILETYPES] = {
"???", "OBJ", "BTX", "BSD", "BRD", "RB ",
"???", "LIB", "???", "???", "SYS", "GR "
};
static uint8_t diskImage[IMGSIZE];
static char imgFileName[256] = DEFAULTIMAGE;
/* ------- Sector I/O helpers ------- */
/* Convert logical sector to byte offset in raw image */
static int sec_offset(int logical_sec)
{
/* Logical sector mapping:
* phys_sector = logical % 16 + 1 (but stored 0-based in image)
* head = (logical / 16) % 2
* cylinder = logical / 16 / 2
* Raw image is sequential: cyl0/head0/sec1..16, cyl0/head1/sec1..16, ... */
return logical_sec * SECSIZE;
}
/* Invert a buffer (XOR 0xFF) — MZ-700 data bus inversion */
static void invert_buf(uint8_t *buf, int len)
{
for (int i = 0; i < len; i++)
buf[i] ^= 0xFF;
}
/* Read a logical sector from image into buffer (un-inverted) */
static void read_sector(int logical_sec, uint8_t *buf)
{
memcpy(buf, &diskImage[sec_offset(logical_sec)], SECSIZE);
invert_buf(buf, SECSIZE);
}
/* Write a buffer to a logical sector in image (inverts before writing) */
static void write_sector(int logical_sec, const uint8_t *buf)
{
memcpy(&diskImage[sec_offset(logical_sec)], buf, SECSIZE);
invert_buf(&diskImage[sec_offset(logical_sec)], SECSIZE);
}
/* Load disk image from file */
static int load_image(void)
{
FILE *f = fopen(imgFileName, "rb");
if (!f) {
fprintf(stderr, "ERROR: Cannot open '%s'\n", imgFileName);
return 0;
}
size_t n = fread(diskImage, 1, IMGSIZE, f);
fclose(f);
if (n != IMGSIZE) {
fprintf(stderr, "ERROR: '%s' is not a valid disk image (%zu bytes, expected %d)\n",
imgFileName, n, IMGSIZE);
return 0;
}
return 1;
}
/* Save disk image to file */
static int save_image(void)
{
FILE *f = fopen(imgFileName, "wb");
if (!f) {
fprintf(stderr, "ERROR: Cannot write '%s'\n", imgFileName);
return 0;
}
fwrite(diskImage, IMGSIZE, 1, f);
fclose(f);
return 1;
}
/* Format a filename for display: stop at CR, null-terminate */
static void format_name(char *dst, const char *src, int maxlen)
{
memcpy(dst, src, maxlen);
dst[maxlen] = '\0';
for (int i = 0; i < maxlen; i++) {
if (dst[i] == '\r' || dst[i] == '\0') { dst[i] = '\0'; break; }
}
}
/* ------- Commands ------- */
/* Format: create an empty formatted disk image */
static void cmd_format(void)
{
uint8_t sec[SECSIZE];
printf("Formatting '%s' (%d bytes, %d sectors)\n", imgFileName, IMGSIZE, TOTAL_SECTORS);
/* Fill entire image with 0xFF (which inverts to 0x00 = empty) */
memset(diskImage, 0xFF, IMGSIZE);
/* Write volume header in directory sector 16, entry 0 */
memset(sec, 0, SECSIZE);
sec[0] = 0x81; /* Volume header marker */
sec[1] = 'O'; /* Volume type */
/* +14: capacity hint = 100 (not used by RFS) */
sec[0x14] = 100;
/* +1E: next free sector (first data sector) */
sec[0x1E] = DATA_FIRST_SEC & 0xFF;
sec[0x1F] = DATA_FIRST_SEC >> 8;
write_sector(DIR_FIRST_SEC, sec);
if (!save_image()) exit(1);
puts("Done.");
}
/* Dir: list directory contents */
static void cmd_dir(void)
{
uint8_t sec[SECSIZE];
char name[18];
int files = 0;
if (!load_image()) exit(1);
/* Check boot sector */
read_sector(0, sec);
struct BootSector *boot = (struct BootSector *)sec;
if (boot->type == BOOT_SIG_TYPE && memcmp(boot->signature, BOOT_SIG, BOOT_SIG_LEN) == 0) {
format_name(name, boot->name, 11);
printf("\nBoot program: %-17s Size=%u Load=0x%04X Sector=%u\n",
name, boot->fileSize, boot->loadAddress, boot->sectorAddress);
} else {
printf("\nDisk is not bootable.\n");
}
printf("\nDirectory of '%s':\n\n", imgFileName);
printf(" %-17s %-3s %5s %s %4s %4s %6s (C H S)\n",
"Name", "Typ", "Size", "L", "Load", "Exec", "Sector");
printf(" %-17s %-3s %5s %s %4s %4s %6s %s\n",
"-----------------", "---", "-----", "-", "----", "----", "------", "-------");
for (int ds = 0; ds < DIR_SECTORS; ds++) {
read_sector(DIR_FIRST_SEC + ds, sec);
for (int e = 0; e < DIR_ENTRIES; e++) {
/* Skip entry 0 of first directory sector (volume header) */
if (ds == 0 && e == 0) continue;
struct DirEntry *ent = (struct DirEntry *)&sec[e * sizeof(struct DirEntry)];
if (ent->fileType == 0x00 || ent->fileType >= 0x80)
continue;
uint8_t ft = ent->fileType;
if (ft >= MAXFILETYPES) ft = 0;
format_name(name, ent->fileName, 17);
int ls = ent->sectorAddress;
int ps = ls % 16 + 1;
int hd = (ls / 16) % 2;
int cy = ls / 16 / 2;
printf(" %-17s %s %5u %c %04X %04X %5u (%2d %d %2d)\n",
name, FileTypes[ft], ent->fileSize,
ent->lockFlag ? 'X' : ' ',
ent->loadAddress, ent->execAddress,
ent->sectorAddress, cy, hd, ps);
files++;
}
}
/* Calculate free space */
int highSec = DATA_FIRST_SEC;
for (int ds = 0; ds < DIR_SECTORS; ds++) {
read_sector(DIR_FIRST_SEC + ds, sec);
for (int e = 0; e < DIR_ENTRIES; e++) {
struct DirEntry *ent = (struct DirEntry *)&sec[e * sizeof(struct DirEntry)];
if (ent->fileType > 0 && ent->fileType < 0x80 && ent->sectorAddress > 0) {
int endSec = ent->sectorAddress + (ent->fileSize + SECSIZE - 1) / SECSIZE;
if (endSec > highSec) highSec = endSec;
}
}
}
printf("\n%d file(s), %d sectors used, %d sectors free\n",
files, highSec, TOTAL_SECTORS - highSec);
}
/* Add: add an MZF file to the disk directory */
static void cmd_add(const char *mzfFileName)
{
FILE *mzfFile;
uint8_t sec[SECSIZE];
struct CMTHeader cmtHdr;
char name[18];
if (!load_image()) exit(1);
/* Open and read MZF file */
mzfFile = fopen(mzfFileName, "rb");
if (!mzfFile) {
fprintf(stderr, "ERROR: Cannot open '%s'\n", mzfFileName);
exit(1);
}
fseek(mzfFile, 0, SEEK_END);
long mzfSize = ftell(mzfFile);
fseek(mzfFile, 0, SEEK_SET);
if (mzfSize < CMTHDRSIZE) {
fprintf(stderr, "ERROR: '%s' too small for MZF format (%ld bytes)\n", mzfFileName, mzfSize);
fclose(mzfFile);
exit(1);
}
fread(&cmtHdr, CMTHDRSIZE, 1, mzfFile);
uint16_t dataSize = cmtHdr.size;
if (dataSize == 0) dataSize = (uint16_t)(mzfSize - CMTHDRSIZE);
/* Find free directory entry and highest used sector */
int freeDirSec = -1, freeDirIdx = -1;
int highSec = DATA_FIRST_SEC;
for (int ds = 0; ds < DIR_SECTORS; ds++) {
read_sector(DIR_FIRST_SEC + ds, sec);
for (int e = 0; e < DIR_ENTRIES; e++) {
if (ds == 0 && e == 0) continue; /* Skip volume header */
struct DirEntry *ent = (struct DirEntry *)&sec[e * sizeof(struct DirEntry)];
if (ent->fileType == 0x00 && freeDirSec < 0) {
freeDirSec = ds;
freeDirIdx = e;
}
if (ent->fileType > 0 && ent->fileType < 0x80 && ent->sectorAddress > 0) {
int endSec = ent->sectorAddress + (ent->fileSize + SECSIZE - 1) / SECSIZE;
if (endSec > highSec) highSec = endSec;
}
}
}
if (freeDirSec < 0) {
fprintf(stderr, "ERROR: Directory full\n");
fclose(mzfFile);
exit(1);
}
int dataSectors = (dataSize + SECSIZE - 1) / SECSIZE;
if (highSec + dataSectors > TOTAL_SECTORS) {
fprintf(stderr, "ERROR: Not enough space (need %d sectors, have %d)\n",
dataSectors, TOTAL_SECTORS - highSec);
fclose(mzfFile);
exit(1);
}
/* Write file data sectors */
for (int s = 0; s < dataSectors; s++) {
memset(sec, 0, SECSIZE);
fread(sec, 1, SECSIZE, mzfFile);
write_sector(highSec + s, sec);
}
fclose(mzfFile);
/* Update directory entry */
read_sector(DIR_FIRST_SEC + freeDirSec, sec);
struct DirEntry *ent = (struct DirEntry *)&sec[freeDirIdx * sizeof(struct DirEntry)];
memset(ent, 0, sizeof(struct DirEntry));
ent->fileType = cmtHdr.attribute;
if (ent->fileType == 0x05) ent->fileType = 0x02; /* CMT type 05 → FD type 02 (BTX) */
memcpy(ent->fileName, cmtHdr.name, 17);
ent->fileSize = dataSize;
ent->loadAddress = cmtHdr.loadAddress;
ent->execAddress = cmtHdr.execAddress;
ent->sectorAddress = highSec;
write_sector(DIR_FIRST_SEC + freeDirSec, sec);
format_name(name, cmtHdr.name, 17);
printf(" Added: \"%s\" type=%s size=%u load=0x%04X exec=0x%04X sector=%d\n",
name, FileTypes[ent->fileType < MAXFILETYPES ? ent->fileType : 0],
dataSize, cmtHdr.loadAddress, cmtHdr.execAddress, highSec);
printf(" %d sectors free\n", TOTAL_SECTORS - highSec - dataSectors);
if (!save_image()) exit(1);
}
/* Extract: extract a file from disk to MZF format */
static void cmd_extract(const char *fileName)
{
uint8_t sec[SECSIZE];
char entName[18], outName[256];
if (!load_image()) exit(1);
/* Search directory for matching filename */
static struct DirEntry foundEntry;
int foundIt = 0;
for (int ds = 0; ds < DIR_SECTORS && !foundIt; ds++) {
read_sector(DIR_FIRST_SEC + ds, sec);
for (int e = 0; e < DIR_ENTRIES && !foundIt; e++) {
if (ds == 0 && e == 0) continue;
struct DirEntry *ent = (struct DirEntry *)&sec[e * sizeof(struct DirEntry)];
if (ent->fileType > 0 && ent->fileType < 0x80) {
format_name(entName, ent->fileName, 17);
if (strcmp(fileName, entName) == 0) {
memcpy(&foundEntry, ent, sizeof(foundEntry));
foundIt = 1;
}
}
}
}
if (!foundIt) {
/* Also check boot sector */
read_sector(0, sec);
struct BootSector *boot = (struct BootSector *)sec;
if (boot->type == BOOT_SIG_TYPE && memcmp(boot->signature, BOOT_SIG, BOOT_SIG_LEN) == 0) {
format_name(entName, boot->name, 11);
if (strcmp(fileName, entName) == 0) {
memset(&foundEntry, 0, sizeof(foundEntry));
foundEntry.fileType = 0x01;
memcpy(foundEntry.fileName, boot->name, 11);
foundEntry.fileSize = boot->fileSize;
foundEntry.loadAddress = boot->loadAddress;
foundEntry.sectorAddress = boot->sectorAddress;
foundIt = 1;
}
}
}
if (!foundIt) {
fprintf(stderr, "ERROR: '%s' not found on disk\n", fileName);
exit(1);
}
struct DirEntry *found = &foundEntry;
/* Build output filename */
snprintf(outName, sizeof(outName), "%s.mzf", fileName);
for (char *p = outName; *p; p++) {
if (*p == ' ') *p = '_';
}
/* Write MZF file */
FILE *out = fopen(outName, "wb");
if (!out) {
fprintf(stderr, "ERROR: Cannot create '%s'\n", outName);
exit(1);
}
struct CMTHeader hdr;
memset(&hdr, 0, sizeof(hdr));
hdr.attribute = (found->fileType == 0x02) ? 0x05 : found->fileType;
memcpy(hdr.name, found->fileName, 17);
hdr.size = found->fileSize;
hdr.loadAddress = found->loadAddress;
hdr.execAddress = found->execAddress;
snprintf(hdr.comment, sizeof(hdr.comment), "Extracted by MZFDTool V%s", VERSION);
fwrite(&hdr, sizeof(hdr), 1, out);
/* Read and write data sectors */
uint16_t remaining = found->fileSize;
int curSec = found->sectorAddress;
while (remaining > 0) {
read_sector(curSec, sec);
uint16_t chunk = (remaining > SECSIZE) ? SECSIZE : remaining;
fwrite(sec, 1, chunk, out);
remaining -= chunk;
curSec++;
}
fclose(out);
format_name(entName, found->fileName, 17);
printf(" Extracted: \"%s\" → %s (%u bytes)\n", entName, outName, found->fileSize);
}
/* Boot: set boot program from MZF file */
static void cmd_boot(const char *mzfFileName)
{
FILE *mzfFile;
uint8_t sec[SECSIZE];
struct CMTHeader cmtHdr;
char name[18];
if (!load_image()) exit(1);
mzfFile = fopen(mzfFileName, "rb");
if (!mzfFile) {
fprintf(stderr, "ERROR: Cannot open '%s'\n", mzfFileName);
exit(1);
}
fseek(mzfFile, 0, SEEK_END);
long mzfSize = ftell(mzfFile);
fseek(mzfFile, 0, SEEK_SET);
if (mzfSize < CMTHDRSIZE) {
fprintf(stderr, "ERROR: '%s' too small for MZF format\n", mzfFileName);
fclose(mzfFile);
exit(1);
}
fread(&cmtHdr, CMTHDRSIZE, 1, mzfFile);
uint16_t dataSize = cmtHdr.size;
if (dataSize == 0) dataSize = (uint16_t)(mzfSize - CMTHDRSIZE);
/* Find space for boot data — use sectors 1-15 (logical), before directory */
int dataSectors = (dataSize + SECSIZE - 1) / SECSIZE;
int bootDataStart = 1; /* Sector 1 (sector 0 is the boot sector itself) */
if (dataSectors > 15) {
fprintf(stderr, "ERROR: Boot program too large (%d sectors, max 15)\n", dataSectors);
fclose(mzfFile);
exit(1);
}
/* Write boot data to sectors 1-15 */
for (int s = 0; s < dataSectors; s++) {
memset(sec, 0, SECSIZE);
fread(sec, 1, SECSIZE, mzfFile);
write_sector(bootDataStart + s, sec);
}
fclose(mzfFile);
/* Write boot sector (sector 0) */
memset(sec, 0, SECSIZE);
struct BootSector *boot = (struct BootSector *)sec;
boot->type = BOOT_SIG_TYPE;
memcpy(boot->signature, BOOT_SIG, BOOT_SIG_LEN);
format_name(name, cmtHdr.name, 11);
memcpy(boot->name, cmtHdr.name, 11);
boot->loadAddress = cmtHdr.loadAddress;
boot->fileSize = dataSize;
boot->sectorAddress = bootDataStart;
write_sector(0, sec);
format_name(name, cmtHdr.name, 11);
printf(" Boot set: \"%s\" size=%u load=0x%04X exec=0x%04X sector=%d\n",
name, dataSize, cmtHdr.loadAddress, cmtHdr.execAddress, bootDataStart);
if (!save_image()) exit(1);
}
/* ------- Usage and main ------- */
static void usage(void)
{
printf("Usage:\n");
printf(" MZFDTool format [-o disk.img] Format empty disk image\n");
printf(" MZFDTool dir [-o disk.img] List directory\n");
printf(" MZFDTool add <file.mzf> [-o disk.img] Add MZF file to disk\n");
printf(" MZFDTool extract <name> [-o disk.img] Extract file to MZF\n");
printf(" MZFDTool boot <file.mzf> [-o disk.img] Set boot program\n");
printf("\n");
printf("Options:\n");
printf(" -o <filename> Disk image file (default: %s)\n", DEFAULTIMAGE);
printf("\n");
printf("Disk geometry: %d cyls, %d heads, %d sectors, %d bytes/sector = %d bytes\n",
CYLS, HEADS, SECTORS, SECSIZE, IMGSIZE);
printf("\n");
}
int main(int argc, char *argv[])
{
printf("\nMZFDTool V%s (c) 2002 BKK, 2026 Philip Smart\n\n", VERSION);
if (argc < 2) {
usage();
return 1;
}
/* Parse -o option from any position */
for (int i = 1; i < argc - 1; i++) {
if (strcmp("-o", argv[i]) == 0) {
strncpy(imgFileName, argv[i + 1], sizeof(imgFileName) - 1);
imgFileName[sizeof(imgFileName) - 1] = '\0';
for (int j = i; j < argc - 2; j++)
argv[j] = argv[j + 2];
argc -= 2;
break;
}
}
if (strcmp("format", argv[1]) == 0) {
cmd_format();
} else if (strcmp("dir", argv[1]) == 0) {
cmd_dir();
} else if (strcmp("add", argv[1]) == 0) {
if (argc < 3) {
fprintf(stderr, "ERROR: No MZF file specified\n");
return 2;
}
cmd_add(argv[2]);
} else if (strcmp("extract", argv[1]) == 0) {
if (argc < 3) {
fprintf(stderr, "ERROR: No filename specified\n");
return 2;
}
cmd_extract(argv[2]);
} else if (strcmp("boot", argv[1]) == 0) {
if (argc < 3) {
fprintf(stderr, "ERROR: No MZF file specified\n");
return 2;
}
cmd_boot(argv[2]);
} else {
fprintf(stderr, "ERROR: Unknown command '%s'\n", argv[1]);
usage();
return 1;
}
return 0;
}

630
tools/MZFD/MZFDTool.c.original vendored Executable file
View File

@@ -0,0 +1,630 @@
#define VERSION "1.01"
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
#include <mem.h>
#include <dos.h>
union REGS regs;
struct SREGS sregs;
struct MZ700BootStruct
{
unsigned char Type;
char Signature[6];
char Name[11];
unsigned short StartAddress;
unsigned short FileSize;
unsigned char Dummy[8];
unsigned short SectorAddress;
};
struct MZ700DirectoryStruct
{
unsigned char FileType; // 0x00 -> 1
char FileName[17]; // 0x01 ... 0x11 -> 17
unsigned char LockFlag; // 0x12 -> 1
unsigned char DummyFlag; // 0x13 -> 1
unsigned short FileSize; // 0x14 ... 0x15 -> 2
unsigned short LoadAddress; // 0x16 ... 0x17 -> 2
unsigned short ExecAddress; // 0x18 ... 0x19 -> 2
char Dummy[4]; // 0x1A ... 0x1D -> 4
unsigned short SectorAddress; // 0x1E ... 0x1F -> 2
};
struct CMTHeader
{
unsigned char Attribute;
char Name[17];
unsigned short Size;
unsigned short LoadAddress;
unsigned short ExecAddress;
char Comment[104];
};
char FileTypes[][4] = {
"???",
"OBJ",
"BTX",
"BSD",
"BRD",
"RB ",
"???",
"LIB",
"???",
"???",
"SYS",
"GR "
};
unsigned char DiskParameters[11] =
{0xDF, 0x02, 0x25, 0x01, 16, 0x4E, 0xFF, 0x6C, 0xE5, 100, 8};
// {0xDF, 0x02, 0x25, 0x02, 18, 0x1B, 0xFF, 0x6C, 0xF6, 100, 8}; /* 1.44MB */
char *ErrorMsg[] =
{
"success",
"invalid function",
"address mark not found",
"disk write-protected",
"sector not found / read error",
"reset faild",
"data did not verify correctly",
"disk changed",
"drive parameter activity failed",
"DMA overrun",
"data boundary error",
"bad sector detected",
"bad track detected",
"unsupported track or invalid media",
"invalid number of sectors",
"control data address mark detected",
"DMA arbitration level out of range",
"uncorrectable CRC or ECCerror",
"data ECC corrected"
};
unsigned char SectorBuffer[1024];
unsigned char Drive;
unsigned char Cylinder;
unsigned char Head;
unsigned char Sector;
unsigned int OrgInt1EOffset;
unsigned int OrgInt1ESegment;
void Error(unsigned char Index)
{
printf("Error: %02X - ", Index);
if(Index < 0x12) printf("%s\n", ErrorMsg[Index]);
if(Index == 0x80) puts("No disc in drive!");
exit(1);
}
void ShowSectorBuffer(void)
{
unsigned char ByteCounter;
unsigned char LineCounter;
int Counter;
Counter = 0;
for(LineCounter = 0; LineCounter < 16; LineCounter++)
{
printf("\n%04X : ", Counter);
for(ByteCounter = 0; ByteCounter < 16; ByteCounter++)
{
printf("%02X ", SectorBuffer[Counter + ByteCounter]);
}
printf(" ");
for(ByteCounter = 0; ByteCounter < 16; ByteCounter++)
{
printf("%c", iscntrl(SectorBuffer[Counter]) ? ' ' : SectorBuffer[Counter]);
Counter++;
}
}
puts("");
}
int ResetFloppy(char Drive)
{
regs.h.ah = 0;
regs.h.dl = Drive;
int86(0x13, &regs, &regs);
return(regs.h.ah);
}
int ReadSector(char Drive, char Cylinder, char Head, char Sector)
{
int Counter;
if(Head == 0) Head = 1;
else Head = 0;
regs.h.ah = 0x02;
Counter = 3;
while((regs.h.ah != 0) && (Counter != 0))
{
regs.h.ah = 0x02;
regs.h.al = 1;
regs.h.ch = Cylinder;
regs.h.cl = Sector;
regs.h.dh = Head;
regs.h.dl = Drive;
sregs.es = FP_SEG(SectorBuffer);
regs.x.bx = FP_OFF(SectorBuffer);
int86x(0x13, &regs, &regs, &sregs);
Counter--;
}
for(Counter = 0; Counter < sizeof(SectorBuffer); Counter++)
SectorBuffer[Counter] = ~SectorBuffer[Counter];
return(regs.x.ax);
}
void SaveInt1E(void)
{
OrgInt1EOffset = peek(0x0000, 0x1E * 4);
OrgInt1ESegment = peek(0x0000, 0x1E * 4 + 2);
}
void SetInt1E(unsigned char Type)
{
Type++;
asm cli;
poke(0x0000, 0x1E * 4, FP_OFF(DiskParameters));
poke(0x0000, 0x1E * 4 + 2, FP_SEG(DiskParameters));
asm sti;
}
void ResetInt1E(void)
{
asm cli;
poke(0x0000, 0x1E * 4, OrgInt1EOffset);
poke(0x0000, 0x1E * 4 + 2, OrgInt1ESegment);
asm sti;
}
void ShowDirectory(void)
{
unsigned char SectorCounter;
unsigned char Counter;
unsigned char C, H, S;
struct MZ700BootStruct *Boot;
struct MZ700DirectoryStruct *Entry;
char FileName[18];
unsigned int Result;
// unsigned short LastUsedSector;
puts("");
Result = ReadSector(Drive, 0, 0, 1);
Result = Result >> 8;
if(Result != 0) Error(Result);
Boot = (struct MZ700BootStruct *) SectorBuffer;
memcpy(FileName, Boot->Signature, 6);
FileName[6] = '\0';
if((Boot->Type == 3) && (strcmp(FileName, "IPLPRO") == 0))
{
puts("Floppy is bootable:");
puts("");
memcpy(FileName, Boot->Name, 11);
if(strchr(FileName, 0x0D) != NULL) *strchr(FileName, 0x0D) = '\0';
else FileName[11] = '\0';
printf(" %-17s OBJ %5u ", FileName, Boot->FileSize);
S = Boot->SectorAddress % 16 + 1;
H = (Boot->SectorAddress / 16) % 2;
C = Boot->SectorAddress / 16 / 2;
printf(" %4u (%2u %u %2u)\n", Boot->SectorAddress, C, H, S);
}
else
{
puts("Floppy is not bootable.");
}
puts("");
puts("Directory:");
puts("");
puts(" Name Type Size Lock Load Exec Pos ( C H S)");
puts("");
for(SectorCounter = 1; SectorCounter < 17; SectorCounter++)
{
ReadSector(Drive, 0, 1, SectorCounter);
for(Counter = 0; Counter < 256 / 32; Counter++)
{
Entry = (struct MZ700DirectoryStruct *) &SectorBuffer[Counter * sizeof(struct MZ700DirectoryStruct)];
// if((SectorCounter == 1) && (Counter == 0)) LastUsedSector = Entry->SectorAddress;
if((Entry->FileType > 0) && (Entry->FileType < 0x80))
{
memcpy(FileName, Entry->FileName, 17);
if(strchr(FileName, 0x0D) != NULL) *strchr(FileName, 0x0D) = '\0';
else FileName[17] = '\0';
printf(" %-17s %s %5u %c %04X %04X", FileName, FileTypes[Entry->FileType], Entry->FileSize, Entry->LockFlag ? 'X' : ' ', Entry->LoadAddress, Entry->ExecAddress);
S = Entry->SectorAddress % 16 + 1;
H = (Entry->SectorAddress / 16) % 2;
C = Entry->SectorAddress / 16 / 2;
printf(" %4u (%2u %u %2u)\n", Entry->SectorAddress, C, H, S);
}
}
}
// printf("\nLast used Sector: %u\n\n", LastUsedSector);
}
void CopyFDToMZF(char *FileName)
{
char MZFFileName[13];
char EntryFileName[18];
unsigned char SectorCounter, Counter;
FILE *MZFFile;
struct MZ700DirectoryStruct *Entry;
struct CMTHeader MZFHeader;
unsigned char C, H, S;
unsigned short WriteSize;
unsigned int Result;
S = 0;
Result = ReadSector(Drive, 0, 0, 1); // BootSector
Result = Result >> 8;
if(Result != 0) Error(Result);
SectorBuffer[33] = '\0'; // emergency stop
if(strstr(SectorBuffer, FileName) != NULL)
{
Entry = (struct MZ700DirectoryStruct *) SectorBuffer;
S = Entry->SectorAddress % 16 + 1;
H = (Entry->SectorAddress / 16) % 2;
C = Entry->SectorAddress / 16 / 2;
Entry->FileType = 1;
}
else
{
for(SectorCounter = 1; (SectorCounter < 17) && (S == 0); SectorCounter++)
{
ReadSector(Drive, 0, 1, SectorCounter);
for(Counter = 0; (Counter < 256 / 32) && (S == 0); Counter++)
{
Entry = (struct MZ700DirectoryStruct *) &SectorBuffer[Counter * sizeof(struct MZ700DirectoryStruct)];
if((Entry->FileType > 0) && (Entry->FileType < 0x80))
{
memcpy(EntryFileName, Entry->FileName, 17);
if(strchr(EntryFileName, 0x0D) != NULL) *strchr(EntryFileName, 0x0D) = '\0';
else EntryFileName[17] = '\0';
if(strcmp(FileName, EntryFileName) == 0)
{
S = Entry->SectorAddress % 16 + 1;
H = (Entry->SectorAddress / 16) % 2;
C = Entry->SectorAddress / 16 / 2;
}
}
}
}
}
if(S != 0)
{
memset(MZFFileName, 0, sizeof(MZFFileName));
strncpy(MZFFileName, FileName, 8);
strcat(MZFFileName, ".MZF");
printf("%s found save it as %s\n", FileName, MZFFileName);
MZFFile = fopen(MZFFileName , "wb");
memset(&MZFHeader, 0, sizeof(MZFHeader));
if(Entry->FileType == 0x02) MZFHeader.Attribute = 0x05;
else MZFHeader.Attribute = Entry->FileType;
memcpy(MZFHeader.Name, Entry->FileName, 17);
MZFHeader.Size = Entry->FileSize;
strcat(MZFHeader.Comment, "by MZFDTool");
if((Entry->FileType == 0x01) && (Entry->LoadAddress == 0x0000))
{
MZFHeader.Comment[24] = 0x21;
MZFHeader.Comment[25] = 0x30;
MZFHeader.Comment[26] = 0x11;
MZFHeader.Comment[27] = 0x01;
MZFHeader.Comment[28] = 0x10;
MZFHeader.Comment[29] = 0x00;
MZFHeader.Comment[30] = 0x11;
MZFHeader.Comment[31] = 0xF0;
MZFHeader.Comment[32] = 0xCF;
MZFHeader.Comment[33] = 0xED;
MZFHeader.Comment[34] = 0xB0;
MZFHeader.Comment[35] = 0xC3;
MZFHeader.Comment[36] = 0xF0;
MZFHeader.Comment[37] = 0xCF;
MZFHeader.Comment[40] = 0x21;
MZFHeader.Comment[41] = 0x00;
MZFHeader.Comment[42] = 0x12;
MZFHeader.Comment[43] = 0x01;
MZFHeader.Comment[44] = (unsigned char) Entry->FileSize & 0xFF;
MZFHeader.Comment[45] = Entry->FileSize >> 8;
MZFHeader.Comment[46] = 0x11;
MZFHeader.Comment[47] = 0x00;
MZFHeader.Comment[48] = 0x00;
MZFHeader.Comment[49] = 0xD3;
MZFHeader.Comment[50] = 0xE0;
MZFHeader.Comment[51] = 0xED;
MZFHeader.Comment[52] = 0xB0;
MZFHeader.Comment[53] = 0xC3;
MZFHeader.Comment[54] = 0x00;
MZFHeader.Comment[55] = 0x00;
MZFHeader.LoadAddress = 0x1200;
MZFHeader.ExecAddress = 0x1120;
}
else
{
MZFHeader.LoadAddress = Entry->LoadAddress;
MZFHeader.ExecAddress = Entry->ExecAddress;
}
fwrite(&MZFHeader, sizeof(MZFHeader), 1, MZFFile);
while(MZFHeader.Size != 0)
{
WriteSize = 256;
if(MZFHeader.Size < WriteSize) WriteSize = MZFHeader.Size;
MZFHeader.Size = MZFHeader.Size - WriteSize;
ReadSector(Drive, C, H, S);
fwrite(SectorBuffer, WriteSize, 1, MZFFile);
S++;
if(S == 17)
{
S = 1;
if(H == 0) H = 1;
else
{
C++;
H = 0;
}
}
}
fclose(MZFFile);
}
else
{
puts("");
printf("%s not found!\n", FileName);
puts("");
puts("Remember that capital letters are differenced!");
}
}
void CopyMZFToFD(char *FileName)
{
printf("Not implemented yet (%s)\n", FileName);
}
void Copy(char *FileName)
{
if((strstr(FileName, ".MZF") == NULL) && (strstr(FileName, ".mzf") == NULL)) CopyFDToMZF(FileName);
else CopyMZFToFD(FileName);
}
void ShowMap(void)
{
unsigned short UsedSectors;
unsigned short Counter;
unsigned char BitCounter;
// unsigned char BitMask;
unsigned short BitMask;
unsigned short *SectorsPerTrack;
unsigned char StartTrack;
unsigned int Result;
puts("");
Result = ReadSector(Drive, 0, 0, 16);
Result = Result >> 8;
if(Result != 0) Error(Result);
UsedSectors = SectorBuffer[3] * 256 + SectorBuffer[4];
StartTrack = SectorBuffer[5];
printf("Volume: %c%c%c Used Sectors: %4u\n\n", SectorBuffer[0], SectorBuffer[1], SectorBuffer[2], UsedSectors);
puts("");
puts(" 1111111");
puts("Track ( C H S) 1234567890123456");
puts("");
for(Counter = StartTrack; Counter <= 80; Counter++)
{
printf(" %3d (%2d %s 1) ", Counter, (Counter - 1) / 2, (Counter - 1) % 2 ? "1" : "0");
BitMask = 0x0001;
SectorsPerTrack = (unsigned short *) &SectorBuffer[6 + 2 * (Counter - StartTrack)];
for(BitCounter = 0; BitCounter < 16; BitCounter++)
{
if(*SectorsPerTrack & BitMask) printf("X");
else printf("-");
BitMask = BitMask << 1;
}
puts("");
}
puts("");
}
void Init(void)
{
memset(SectorBuffer, 0, sizeof(SectorBuffer));
Drive = 0;
Cylinder = 0;
Head = 0;
Sector = 1;
}
void Usage(void)
{
puts("usage: mzfdtool dir [DRIVE:]");
puts(" map [DRIVE:]");
puts(" copy [DRIVE:] FILENAME");
puts(" DRIVE: C H S");
puts("");
exit(1);
}
int main(int argc, char *argv[])
{
unsigned int Result;
printf("\nMZFDTool V %s (c) 2002 by BKK\n\n", VERSION);
if(argc == 1) Usage();
Init();
SetInt1E(0);
ResetFloppy(Drive);
if(strcmp(argv[1], "dir") == 0)
{
if(argc == 3)
if(toupper(argv[2][0]) == 'B') Drive = 1;
ShowDirectory();
}
else
{
if(strcmp(argv[1], "copy") == 0)
{
if(argc == 4)
{
if(toupper(argv[2][0]) == 'B') Drive = 1;
Copy(argv[3]);
}
else Copy(argv[2]);
}
else
{
if(strcmp(argv[1], "map") == 0)
{
if(argc == 3)
if(toupper(argv[2][0]) == 'B') Drive = 1;
ShowMap();
}
else
{
if(argc < 3) exit(1);
if(toupper(argv[1][0]) == 'B') Drive = 1;
Cylinder = atoi(argv[2]);
Head = atoi(argv[3]);
Sector = atoi(argv[4]);
printf("Drive: %c CHS: %u %u %u\n", Drive ? 'B' : 'A', Cylinder, Head, Sector);
// Set_Floppy_Medis_Type();
Result = ReadSector(Drive, Cylinder, Head, Sector);
Result = Result >> 8;
if(Result == 0) ShowSectorBuffer();
else Error(Result);
}
}
}
ResetInt1E();
ResetFloppy(Drive);
return(0);
}

19
tools/MZFD/Makefile vendored Normal file
View File

@@ -0,0 +1,19 @@
# MZFDTool - Sharp MZ-700 Floppy Disk image tool
# (c) 2002 BKK, 2026 Philip Smart
#
# Usage:
# make Build MZFDTool
# make clean Remove build artifacts
CC = gcc
CFLAGS = -Wall -Wno-unused-result -O2
TARGET = MZFDTool
SRC = MZFDTool.c
$(TARGET): $(SRC)
$(CC) $(CFLAGS) -o $@ $<
clean:
rm -f $(TARGET)
.PHONY: clean

BIN
tools/MZFDTool vendored Executable file

Binary file not shown.

BIN
tools/MZQD/MZQDTool vendored Executable file

Binary file not shown.

366
tools/MZQD/MZQDTool.c Executable file
View File

@@ -0,0 +1,366 @@
/* =========================================================================
* MZQDTool — Sharp MZ-700 Quick Disk (QD) image tool
*
* Creates and manages QD disk images for the MZ-700 Quick Disk system.
* Supports format, directory listing, and adding MZF files to QD images.
*
* Original V0.30 (c) 2002 by BKK
* Updated V1.00 (c) 2026 Philip Smart — rewritten with configurable disk
* name, fixed file ordering, correct CMT→QD header conversion,
* and robust error handling.
*
* QD disk image format (61455 bytes):
*
* Disk header:
* [00] [16 16] [A5] [block_count] [CRC: 43 52 43]
*
* Per file (2 blocks each: header + data):
*
* Header block:
* [00] [16 16] [A5] [00] [size_lo=40] [size_hi=00]
* [64 bytes: QDHeaderStr]
* [CRC: 43 52 43]
*
* Data block:
* [00] [16 16] [A5] [05] [size_lo] [size_hi]
* [file data]
* [CRC: 43 52 43]
*
* Usage:
* MZQDTool format [-o disk.qd]
* MZQDTool dir [-o disk.qd]
* MZQDTool add <file.mzf> [-o disk.qd]
* ========================================================================= */
#define VERSION "1.00"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#define QDSIZE 61455
#define MAXQDFILETYPES 12
#define DEFAULTQDFILE "MZ700.qd"
#define CMTHDRSIZE 128 /* MZF/CMT file header size */
#define QDHDRSIZE 64 /* QD file header size */
/* MZF/CMT tape file header (128 bytes) */
struct CMTHeaderStr {
uint8_t Attribute; /* File type: 01=OBJ, 02=BTX, etc. */
char Name[17]; /* Filename (CR-terminated, null-padded) */
uint16_t Size; /* Data size (0 = use actual file size) */
uint16_t LoadAddress; /* Load address (DTADR) */
uint16_t ExecAddress; /* Execution address (EXADR) */
char Comment[104]; /* Comment area */
};
/* QD file header (64 bytes, stored in header block) */
struct QDHeaderStr {
uint8_t Attribute; /* File type: 01=OBJ, 02=BTX, etc. */
char Name[17]; /* Filename (CR-terminated, null-padded) */
uint16_t Unknown; /* Reserved (usually 0) */
uint16_t Size; /* Data size in bytes */
uint16_t LoadAddress; /* Load address */
uint16_t ExecAddress; /* Execution address */
char Comment[38]; /* Comment (truncated from CMT's 104) */
};
static const char *QDFileTypes[MAXQDFILETYPES] = {
"???", "OBJ", "BTX", "BSD", "BRD", "RB ",
"???", "LIB", "???", "???", "SYS", "GR "
};
static uint8_t QDArray[QDSIZE];
static char qdFileName[256] = DEFAULTQDFILE;
/* ------- Low-level QD block I/O helpers ------- */
static void write_sync(uint16_t *ptr)
{
QDArray[(*ptr)++] = 0x00; /* BREAK */
QDArray[(*ptr)++] = 0x16; /* SYNC */
QDArray[(*ptr)++] = 0x16; /* SYNC */
QDArray[(*ptr)++] = 0xA5; /* SHARP SYNC */
}
static void write_crc(uint16_t *ptr)
{
QDArray[(*ptr)++] = 'C';
QDArray[(*ptr)++] = 'R';
QDArray[(*ptr)++] = 'C';
}
static int save_qd(void)
{
FILE *f = fopen(qdFileName, "wb");
if (!f) {
fprintf(stderr, "ERROR: Cannot write '%s'\n", qdFileName);
return 0;
}
fwrite(QDArray, QDSIZE, 1, f);
fclose(f);
return 1;
}
static int load_qd(void)
{
FILE *f = fopen(qdFileName, "rb");
if (!f) {
fprintf(stderr, "ERROR: Cannot open '%s'\n", qdFileName);
return 0;
}
fread(QDArray, QDSIZE, 1, f);
fclose(f);
return 1;
}
/* ------- Commands ------- */
/* Format: create an empty QD image with 0 files */
static void QDFormat(void)
{
uint16_t ptr = 0;
printf("Formatting '%s' (%d bytes)\n", qdFileName, QDSIZE);
memset(QDArray, 0x00, QDSIZE);
/* Disk header: sync + block_count(0) + CRC */
write_sync(&ptr);
QDArray[ptr++] = 0x00; /* 0 blocks */
write_crc(&ptr);
if (!save_qd()) exit(1);
puts("Done.");
}
/* Dir: list files on the QD */
static void QDDir(void)
{
uint16_t ptr, blockSize;
uint8_t blocks, i;
if (!load_qd()) exit(1);
/* Disk header: 4 sync bytes + block_count + 3 CRC */
ptr = 4;
blocks = QDArray[ptr++];
ptr += 3; /* skip CRC */
printf("Directory of '%s': %d block(s), %d file(s)\n\n",
qdFileName, blocks, blocks / 2);
for (i = 0; i < blocks; i++) {
if (ptr + 7 >= QDSIZE) {
fprintf(stderr, "WARNING: Truncated QD image at block %d\n", i);
break;
}
ptr += 4; /* sync: 00 16 16 A5 */
uint8_t blockType = QDArray[ptr++];
blockSize = QDArray[ptr] | (QDArray[ptr + 1] << 8);
ptr += 2;
if (blockType == 0x00 && blockSize == QDHDRSIZE) {
/* File header block */
struct QDHeaderStr *hdr = (struct QDHeaderStr *)&QDArray[ptr];
uint8_t ft = hdr->Attribute;
if (ft >= MAXQDFILETYPES) ft = 0;
/* Format name: stop at CR or end of field */
char name[18];
memcpy(name, hdr->Name, 17);
name[17] = '\0';
for (int j = 0; j < 17; j++) {
if (name[j] == '\r' || name[j] == '\0') { name[j] = '\0'; break; }
}
printf(" %-3s %-17s Size=%-6u Load=0x%04X Exec=0x%04X\n",
QDFileTypes[ft], name, hdr->Size,
hdr->LoadAddress, hdr->ExecAddress);
} else if (blockType == 0x05) {
/* Data block — skip silently */
} else {
printf(" [block %d: type=0x%02X size=%u]\n", i, blockType, blockSize);
}
ptr += blockSize;
ptr += 3; /* skip CRC */
}
printf("\n%u bytes free\n", QDSIZE - ptr);
}
/* Add: append an MZF file to the QD image */
static void AddFileToQD(const char *mzfFileName)
{
FILE *mzfFile;
uint16_t ptr, fileDataSize;
uint8_t blocks, i;
uint16_t blockSize;
struct CMTHeaderStr cmtHdr;
if (!load_qd()) exit(1);
/* Read block count and skip to end of existing data */
ptr = 4;
blocks = QDArray[ptr];
ptr = 5 + 3; /* past block_count + CRC */
for (i = 0; i < blocks; i++) {
ptr += 4; /* sync */
ptr++; /* block type */
blockSize = QDArray[ptr] | (QDArray[ptr + 1] << 8);
ptr += 2;
ptr += blockSize;
ptr += 3; /* CRC */
}
/* Open and read MZF file */
mzfFile = fopen(mzfFileName, "rb");
if (!mzfFile) {
fprintf(stderr, "ERROR: Cannot open '%s'\n", mzfFileName);
exit(1);
}
fseek(mzfFile, 0, SEEK_END);
long mzfTotalSize = ftell(mzfFile);
fseek(mzfFile, 0, SEEK_SET);
if (mzfTotalSize < CMTHDRSIZE) {
fprintf(stderr, "ERROR: '%s' is too small for MZF format (%ld bytes)\n",
mzfFileName, mzfTotalSize);
fclose(mzfFile);
exit(1);
}
fread(&cmtHdr, CMTHDRSIZE, 1, mzfFile);
fileDataSize = (uint16_t)(mzfTotalSize - CMTHDRSIZE);
/* Check available space */
uint32_t needed = (4+1+2+QDHDRSIZE+3) + (4+1+2+(uint32_t)fileDataSize+3);
if (ptr + needed > QDSIZE) {
fprintf(stderr, "ERROR: Not enough space (need %u, have %u)\n",
(unsigned)needed, QDSIZE - ptr);
fclose(mzfFile);
exit(1);
}
/* ---- Write header block ---- */
write_sync(&ptr);
QDArray[ptr++] = 0x00; /* block type: file header */
QDArray[ptr++] = QDHDRSIZE; /* size low */
QDArray[ptr++] = 0x00; /* size high */
struct QDHeaderStr *qdHdr = (struct QDHeaderStr *)&QDArray[ptr];
memset(qdHdr, 0, QDHDRSIZE);
/* Convert CMT header to QD header */
qdHdr->Attribute = cmtHdr.Attribute;
if (qdHdr->Attribute == 0x05)
qdHdr->Attribute = 0x02; /* MZF tape type 05 → QD type 02 (BTX) */
memcpy(qdHdr->Name, cmtHdr.Name, 17);
qdHdr->Unknown = 0;
/*
* Size field handling:
* - If MZF Size > 0: use it directly (normal file)
* - If MZF Size == 0: use actual file data size (convention for
* ROM images where Size=0 means "whole file"). The QD header
* MUST have the real size — the QD loader uses it to determine
* how many bytes to read.
*/
if (cmtHdr.Size != 0)
qdHdr->Size = cmtHdr.Size;
else
qdHdr->Size = fileDataSize;
qdHdr->LoadAddress = cmtHdr.LoadAddress;
qdHdr->ExecAddress = cmtHdr.ExecAddress;
memcpy(qdHdr->Comment, cmtHdr.Comment, 38);
ptr += QDHDRSIZE;
write_crc(&ptr);
/* ---- Write data block ---- */
write_sync(&ptr);
QDArray[ptr++] = 0x05; /* block type: file data */
QDArray[ptr++] = fileDataSize & 0xFF;
QDArray[ptr++] = (fileDataSize >> 8) & 0xFF;
fread(&QDArray[ptr], 1, fileDataSize, mzfFile);
ptr += fileDataSize;
write_crc(&ptr);
fclose(mzfFile);
/* Update block count (+2: one header block, one data block) */
QDArray[4] = blocks + 2;
/* Format name for display */
char name[18];
memcpy(name, cmtHdr.Name, 17);
name[17] = '\0';
for (int j = 0; j < 17; j++) {
if (name[j] == '\r' || name[j] == '\0') { name[j] = '\0'; break; }
}
printf(" Added: type=0x%02X name=\"%s\" size=%u load=0x%04X exec=0x%04X\n",
qdHdr->Attribute, name, qdHdr->Size,
qdHdr->LoadAddress, qdHdr->ExecAddress);
printf(" QD: %d blocks, %u bytes free\n", blocks + 2, QDSIZE - ptr);
if (!save_qd()) exit(1);
}
/* ------- Usage and main ------- */
static void usage(void)
{
printf("Usage:\n");
printf(" MZQDTool format [-o disk.qd] Format empty QD image\n");
printf(" MZQDTool dir [-o disk.qd] List QD directory\n");
printf(" MZQDTool add <file.mzf> [-o disk.qd] Add MZF file to QD\n");
printf("\n");
printf("Options:\n");
printf(" -o <filename> QD image file (default: %s)\n", DEFAULTQDFILE);
printf("\n");
}
int main(int argc, char *argv[])
{
printf("\nMZQDTool V%s (c) 2002 BKK, 2026 Philip Smart\n\n", VERSION);
if (argc < 2) {
usage();
return 1;
}
/* Parse -o option from any position */
for (int i = 1; i < argc - 1; i++) {
if (strcmp("-o", argv[i]) == 0) {
strncpy(qdFileName, argv[i + 1], sizeof(qdFileName) - 1);
qdFileName[sizeof(qdFileName) - 1] = '\0';
/* Remove -o and its argument from argv for command parsing */
for (int j = i; j < argc - 2; j++)
argv[j] = argv[j + 2];
argc -= 2;
break;
}
}
if (strcmp("format", argv[1]) == 0) {
QDFormat();
} else if (strcmp("dir", argv[1]) == 0) {
QDDir();
} else if (strcmp("add", argv[1]) == 0) {
if (argc < 3) {
fprintf(stderr, "ERROR: No MZF file specified\n");
return 2;
}
AddFileToQD(argv[2]);
} else {
fprintf(stderr, "ERROR: Unknown command '%s'\n", argv[1]);
usage();
return 1;
}
return 0;
}

19
tools/MZQD/Makefile vendored Normal file
View File

@@ -0,0 +1,19 @@
# MZQDTool - Sharp MZ-700 Quick Disk image tool
# (c) 2002 BKK, 2026 Philip Smart
#
# Usage:
# make Build MZQDTool
# make clean Remove build artifacts
CC = gcc
CFLAGS = -Wall -Wno-unused-result -O2
TARGET = MZQDTool
SRC = MZQDTool.c
$(TARGET): $(SRC)
$(CC) $(CFLAGS) -o $@ $<
clean:
rm -f $(TARGET)
.PHONY: clean

BIN
tools/MZQD/side_a/AUTO RUN.mzf vendored Normal file

Binary file not shown.

BIN
tools/MZQD/side_a/BASIC 5Z008A RFS.mzf vendored Normal file

Binary file not shown.

BIN
tools/MZQD/side_a/BASIC 5Z008ADRFS.mzf vendored Normal file

Binary file not shown.

Binary file not shown.

BIN
tools/MZQD/side_a/BASIC MZ-5Z008.mzf vendored Normal file

Binary file not shown.

BIN
tools/MZQD/side_b/DELETE.mzf vendored Normal file

Binary file not shown.

BIN
tools/MZQD/side_b/QDCOPY.mzf vendored Normal file

Binary file not shown.

BIN
tools/MZQD/side_b/TRANS.mzf vendored Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
tools/MZQDTool vendored Executable file

Binary file not shown.

View File

@@ -40,7 +40,7 @@ ASM=glass.jar
BUILDROMLIST="monitor_sa1510_hiload monitor_80c_sa1510_hiload monitor_80c_sa1510 mz80afi monitor_sa1510 monitor_80c_sa1510 monitor_1z-013a monitor_80c_1z-013a ipl" BUILDROMLIST="monitor_sa1510_hiload monitor_80c_sa1510_hiload monitor_80c_sa1510 mz80afi monitor_sa1510 monitor_80c_sa1510 monitor_1z-013a monitor_80c_1z-013a ipl"
#BUILDMZFLIST="hi-ramcheck sharpmz-test" #BUILDMZFLIST="hi-ramcheck sharpmz-test"
BUILDMZFLIST="sa-5510_rfs msbasic_mz80a msbasic_rfs40 msbasic_rfs80 sharpmz-test 1z-013b mz5z009 mz2z009e mz5z008 mz5z008_rfs" BUILDMZFLIST="sa-5510_rfs msbasic_mz80a msbasic_rfs40 msbasic_rfs80 sharpmz-test 1z-013b mz5z009 mz2z009e_orig mz2z009e_rfs mz5z008_orig mz5z008_rfs"
ASMDIR=${ROOTDIR}/asm ASMDIR=${ROOTDIR}/asm
ASMTMPDIR=${ROOTDIR}/tmp ASMTMPDIR=${ROOTDIR}/tmp
INCDIR=${ROOTDIR}/asm/include INCDIR=${ROOTDIR}/asm/include

View File

@@ -147,8 +147,8 @@ cat ${ROM_PATH}/rfs.rom \
# The SFD700 ROM is built as follows: # The SFD700 ROM is built as follows:
# 0x00000 : 0x00FFF - MZ80A Floppy ROM MZ80AFI.rom (duplicated ROM at 0x00400) # 0x00000 : 0x00FFF - MZ80A Floppy ROM MZ80AFI.rom (duplicated ROM at 0x00400)
# 0x01000 : 0x01FFF - MZ-1E05 MZ700 Floppy ROM mz-1e05.rom # 0x01000 : 0x01FFF - MZ-1E05 MZ700 Floppy ROM mz-1e05.rom
# 0x02000 : 0x0BFFF - RFS rfs.rom # 0x02000 : 0x11FFF - RFS rfs.rom (6x4096 pages, 6x8192 pages).
# 0x0C000 : 0x7FFFF - USER / ROM File System # 0x12000 : 0x7FFFF - USER / ROM File System
cat ${ROM_PATH}/mz80afi_sfd700.rom ${ROM_PATH}/mz80afi_sfd700.rom ${ROM_PATH}/mz-1e05.rom ${ROM_PATH}/rfs.rom \ cat ${ROM_PATH}/mz80afi_sfd700.rom ${ROM_PATH}/mz80afi_sfd700.rom ${ROM_PATH}/mz-1e05.rom ${ROM_PATH}/rfs.rom \
>> ${SFD700_ROM} >> ${SFD700_ROM}
@@ -217,13 +217,15 @@ ROM_INCLUDE_RSV1=""
# | | |------- Include in PICOZ80 Build # | | |------- Include in PICOZ80 Build
# | | | |----- Reserved # | | | |----- Reserved
# | | | | |-- Name and path of the sectored version of the MZF file. # | | | | |-- Name and path of the sectored version of the MZF file.
addMZFToROMImageList 1 1 1 0 "${MZB_PATH}/Common/sa-5510_rfs.${SECTORSIZE}.bin" addMZFToROMImageList 1 0 1 0 "${MZB_PATH}/Common/sa-5510_rfs.${SECTORSIZE}.bin"
addMZFToROMImageList 1 1 1 0 "${MZB_PATH}/Common/1z-013b.${SECTORSIZE}.bin" addMZFToROMImageList 1 1 1 0 "${MZB_PATH}/Common/1z-013b.${SECTORSIZE}.bin"
addMZFToROMImageList 1 1 1 0 "${MZB_PATH}/Common/mz2z009e.${SECTORSIZE}.bin" addMZFToROMImageList 1 1 1 0 "${MZB_PATH}/Common/mz2z009e_orig.${SECTORSIZE}.bin"
addMZFToROMImageList 1 1 1 0 "${MZB_PATH}/Common/mz5z008.${SECTORSIZE}.bin" addMZFToROMImageList 1 0 1 0 "${MZB_PATH}/Common/mz2z009e_rfs.${SECTORSIZE}.bin"
addMZFToROMImageList 1 1 1 0 "${MZB_PATH}/Common/mz5z008_orig.${SECTORSIZE}.bin"
addMZFToROMImageList 1 0 1 0 "${MZB_PATH}/Common/mz5z008_rfs.${SECTORSIZE}.bin"
addMZFToROMImageList 1 1 1 0 "${MZB_PATH}/Common/mz5z009.${SECTORSIZE}.bin" addMZFToROMImageList 1 1 1 0 "${MZB_PATH}/Common/mz5z009.${SECTORSIZE}.bin"
addMZFToROMImageList 1 1 1 0 "${MZB_PATH}/Common/msbasic_mz80a.${SECTORSIZE}.bin" addMZFToROMImageList 1 1 1 0 "${MZB_PATH}/Common/msbasic_mz80a.${SECTORSIZE}.bin"
addMZFToROMImageList 1 1 1 0 "${MZB_PATH}/Common/msbasic_rfs40.${SECTORSIZE}.bin" addMZFToROMImageList 1 0 1 0 "${MZB_PATH}/Common/msbasic_rfs40.${SECTORSIZE}.bin"
addMZFToROMImageList 1 0 1 0 "${MZB_PATH}/Common/msbasic_rfs80.${SECTORSIZE}.bin" addMZFToROMImageList 1 0 1 0 "${MZB_PATH}/Common/msbasic_rfs80.${SECTORSIZE}.bin"
# #
addMZFToROMImageList 1 1 1 0 "${MZB_PATH}/Common/basic_sa5510.${SECTORSIZE}.bin" addMZFToROMImageList 1 1 1 0 "${MZB_PATH}/Common/basic_sa5510.${SECTORSIZE}.bin"
@@ -257,7 +259,7 @@ addMZFToROMImageList 1 0 1 0 "${MZB_PATH}/Common/kniforth.${SECTORSIZE}.bin"
addMZFToROMImageList 1 0 1 0 "${MZB_PATH}/Common/tinylispmc.${SECTORSIZE}.bin" addMZFToROMImageList 1 0 1 0 "${MZB_PATH}/Common/tinylispmc.${SECTORSIZE}.bin"
addMZFToROMImageList 1 0 1 0 "${MZB_PATH}/Common/apollo_word_1.9mmc_.${SECTORSIZE}.bin" addMZFToROMImageList 1 0 1 0 "${MZB_PATH}/Common/apollo_word_1.9mmc_.${SECTORSIZE}.bin"
addMZFToROMImageList 1 0 1 0 "${MZB_PATH}/Common/hucalc_80a_m.${SECTORSIZE}.bin" addMZFToROMImageList 1 0 1 0 "${MZB_PATH}/Common/hucalc_80a_m.${SECTORSIZE}.bin"
addMZFToROMImageList 1 0 1 0 "${MZB_PATH}/Common/send-1.${SECTORSIZE}.bin" addMZFToROMImageList 1 1 1 0 "${MZB_PATH}/Common/send-1.${SECTORSIZE}.bin"
addMZFToROMImageList 1 1 1 0 "${MZB_PATH}/Common/apollo_chess_v2a.${SECTORSIZE}.bin" addMZFToROMImageList 1 1 1 0 "${MZB_PATH}/Common/apollo_chess_v2a.${SECTORSIZE}.bin"
addMZFToROMImageList 0 0 0 0 "${MZB_PATH}/Common/5z009-1b.mzf" addMZFToROMImageList 0 0 0 0 "${MZB_PATH}/Common/5z009-1b.mzf"
addMZFToROMImageList 0 0 0 0 "${MZB_PATH}/Common/basic.${SECTORSIZE}.bin" addMZFToROMImageList 0 0 0 0 "${MZB_PATH}/Common/basic.${SECTORSIZE}.bin"