@thlg057/mo5-rag-mcp 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +147 -0
- package/index.js +592 -0
- package/package.json +32 -0
- package/scripts/fd2sd.py +59 -0
- package/scripts/makefd.py +407 -0
- package/scripts/png2mo5.py +478 -0
|
@@ -0,0 +1,407 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
makefd.py - Génère une image disquette .fd bootable pour Thomson MO5
|
|
4
|
+
Remplace : fdfs -addBL output.fd BOOTMO.BIN program.BIN
|
|
5
|
+
|
|
6
|
+
Usage:
|
|
7
|
+
python3 makefd.py output.fd program.BIN [file2.BIN ...]
|
|
8
|
+
|
|
9
|
+
Basé sur fdfs.c d'OlivierP-To8 (https://github.com/OlivierP-To8/BootFloppyDisk)
|
|
10
|
+
Le binaire BOOTMO.BIN est embarqué directement dans ce script.
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
import sys
|
|
14
|
+
import struct
|
|
15
|
+
import os
|
|
16
|
+
import time
|
|
17
|
+
from pathlib import Path
|
|
18
|
+
|
|
19
|
+
# ==============================================================================
|
|
20
|
+
# Constantes du format disquette Thomson MO5
|
|
21
|
+
# ==============================================================================
|
|
22
|
+
SECTOR_SIZE = 256 # octets par secteur (255 utiles)
|
|
23
|
+
SECTOR_BYTES = 255 # octets utiles par secteur
|
|
24
|
+
TRACK_SIZE = 16 * SECTOR_SIZE # 16 secteurs par piste
|
|
25
|
+
DISK_SIZE = 80 * TRACK_SIZE # 80 pistes
|
|
26
|
+
BLOCK_SIZE = 8 * SECTOR_SIZE # 8 secteurs par bloc
|
|
27
|
+
BLOCK_BYTES = 8 * SECTOR_BYTES # octets utiles par bloc
|
|
28
|
+
|
|
29
|
+
# Piste 20 : FAT + répertoire
|
|
30
|
+
FAT_OFFSET = 20 * TRACK_SIZE + SECTOR_SIZE + 1 # secteur 2 de la piste 20
|
|
31
|
+
DIR_OFFSET = 20 * TRACK_SIZE + 2 * SECTOR_SIZE # secteur 3 de la piste 20
|
|
32
|
+
|
|
33
|
+
FREE_BLOCK = 0xff
|
|
34
|
+
RESERVED_BLOCK = 0xfe
|
|
35
|
+
|
|
36
|
+
# ==============================================================================
|
|
37
|
+
# BOOTMO.BIN embarqué (compilé depuis BootMO.asm d'OlivierP-To8)
|
|
38
|
+
# Chargeur de boot pour Thomson MO5, max 120 octets utiles
|
|
39
|
+
# org $2200
|
|
40
|
+
# ==============================================================================
|
|
41
|
+
BOOTMO_BIN = bytes([
|
|
42
|
+
0x00, 0x00, 0x58, 0x22, 0x00, # header Thomson: type=0, size=0x0058, addr=0x2200
|
|
43
|
+
0x10, 0xce, 0x20, 0xcc, # lds #$20CC
|
|
44
|
+
0x86, 0x20, 0x1f, 0x8b, # lda #$20 / tfr a,dp
|
|
45
|
+
0x86, 0x00, 0x97, 0x49, # lda #0 / sta <DKDRV
|
|
46
|
+
0xcc, 0x14, 0x01, # ldd #$1401 (track 20, sector 1)
|
|
47
|
+
0x8e, 0x23, 0x00, # ldx #$2300 (Buffer_)
|
|
48
|
+
0xbd, 0x22, 0x47, # jsr ReadSector_
|
|
49
|
+
0x1f, 0x12, # tfr x,y
|
|
50
|
+
0x31, 0x2d, # leay 13,y
|
|
51
|
+
0xbe, 0x23, 0x0c, # ldx Buffer_+12
|
|
52
|
+
0x30, 0x1b, # Boot_file: leax -5,x
|
|
53
|
+
0x31, 0x21, # Boot_track: leay 1,y
|
|
54
|
+
0xec, 0xa1, # ldd ,y++
|
|
55
|
+
0x81, 0xff, # cmpa #$ff
|
|
56
|
+
0x27, 0x0e, # beq Boot_exec
|
|
57
|
+
0xbd, 0x22, 0x47, # Boot_loop: jsr ReadSector_
|
|
58
|
+
0x30, 0x89, 0x00, 0xff, # leax 255,x
|
|
59
|
+
0x5c, # incb
|
|
60
|
+
0xe1, 0xa4, # cmpb ,y
|
|
61
|
+
0x2e, 0xec, # bgt Boot_track
|
|
62
|
+
0x20, 0xf2, # bra Boot_loop
|
|
63
|
+
0x31, 0x3f, # Boot_exec: leay -1,y
|
|
64
|
+
0xae, 0xa1, # ldx ,y++
|
|
65
|
+
0x34, 0x20, # pshs y
|
|
66
|
+
0xad, 0x84, # jsr ,x
|
|
67
|
+
0x35, 0x20, # puls y
|
|
68
|
+
0xae, 0xa0, # ldx ,y+
|
|
69
|
+
0x8c, 0xff, 0xff, # cmpx #$ffff
|
|
70
|
+
0x26, 0xd7, # bne Boot_file
|
|
71
|
+
0x3f, 0x28, # swi / fcb DKBOOT (reboot)
|
|
72
|
+
# ReadSector_:
|
|
73
|
+
0x34, 0x06, # pshs a,b
|
|
74
|
+
0x97, 0x4b, # sta <DKTRK
|
|
75
|
+
0xd7, 0x4c, # stb <DKSEC
|
|
76
|
+
0x9f, 0x4f, # stx <DKBUF
|
|
77
|
+
0x86, 0x02, # lda #$02
|
|
78
|
+
0x97, 0x48, # sta <DKOPC
|
|
79
|
+
0x3f, 0x26, # swi / fcb DKCO
|
|
80
|
+
0x35, 0x06, # puls a,b
|
|
81
|
+
0x39, # rts
|
|
82
|
+
# footer Thomson
|
|
83
|
+
0xff, 0x00, 0x00, 0x00, 0x00,
|
|
84
|
+
])
|
|
85
|
+
|
|
86
|
+
# ==============================================================================
|
|
87
|
+
# Classe principale
|
|
88
|
+
# ==============================================================================
|
|
89
|
+
class FloppyDisk:
|
|
90
|
+
def __init__(self):
|
|
91
|
+
self.data = bytearray(DISK_SIZE)
|
|
92
|
+
|
|
93
|
+
def format(self, diskname: str = None):
|
|
94
|
+
"""Formate la disquette (équivalent de formatDisk dans fdfs.c)."""
|
|
95
|
+
# Remplissage avec 0xe5
|
|
96
|
+
for i in range(DISK_SIZE):
|
|
97
|
+
self.data[i] = 0xe5
|
|
98
|
+
|
|
99
|
+
# Piste 20 entière initialisée à FREE_BLOCK
|
|
100
|
+
for i in range(20 * TRACK_SIZE, 21 * TRACK_SIZE):
|
|
101
|
+
self.data[i] = FREE_BLOCK
|
|
102
|
+
|
|
103
|
+
# Nom de la disquette sur les 8 premiers octets de la piste 20
|
|
104
|
+
if diskname:
|
|
105
|
+
p = Path(diskname)
|
|
106
|
+
stem = p.stem[:8]
|
|
107
|
+
basename = stem.ljust(8)[:8]
|
|
108
|
+
for i, ch in enumerate(basename):
|
|
109
|
+
self.data[20 * TRACK_SIZE + i] = ord(ch)
|
|
110
|
+
|
|
111
|
+
# Le 1er octet du secteur FAT n'est pas utilisé
|
|
112
|
+
self.data[FAT_OFFSET - 1] = 0x00
|
|
113
|
+
|
|
114
|
+
# Piste 20 réservée (2 blocs)
|
|
115
|
+
self.data[FAT_OFFSET + 2 * 20] = RESERVED_BLOCK
|
|
116
|
+
self.data[FAT_OFFSET + 2 * 20 + 1] = RESERVED_BLOCK
|
|
117
|
+
|
|
118
|
+
# Pistes 80+ à zéro dans la FAT
|
|
119
|
+
for i in range(FAT_OFFSET + 80 * 2, DIR_OFFSET):
|
|
120
|
+
self.data[i] = 0x00
|
|
121
|
+
|
|
122
|
+
# ------------------------------------------------------------------
|
|
123
|
+
def _find_free_block(self, used_blocks: list) -> int:
|
|
124
|
+
"""Trouve un bloc libre, en commençant par la fin (piste 79 → 0)."""
|
|
125
|
+
# Blocs déjà référencés dans le répertoire
|
|
126
|
+
in_use = []
|
|
127
|
+
for entry in range(13, 14 * SECTOR_SIZE, 32):
|
|
128
|
+
block = self.data[DIR_OFFSET + entry]
|
|
129
|
+
if block != FREE_BLOCK:
|
|
130
|
+
in_use.append(block)
|
|
131
|
+
|
|
132
|
+
# Balayage de 79 → 0
|
|
133
|
+
for i in range(79, -1, -1):
|
|
134
|
+
if (self.data[FAT_OFFSET + i] == FREE_BLOCK
|
|
135
|
+
and i not in in_use
|
|
136
|
+
and i not in used_blocks):
|
|
137
|
+
return i
|
|
138
|
+
# Puis 80 → 159
|
|
139
|
+
for i in range(80, 2 * 80):
|
|
140
|
+
if (self.data[FAT_OFFSET + i] == FREE_BLOCK
|
|
141
|
+
and i not in in_use
|
|
142
|
+
and i not in used_blocks):
|
|
143
|
+
return i
|
|
144
|
+
return FREE_BLOCK # disquette pleine
|
|
145
|
+
|
|
146
|
+
def _find_free_entry(self) -> int:
|
|
147
|
+
"""Trouve une entrée libre dans le répertoire."""
|
|
148
|
+
for i in range(0, 14 * SECTOR_SIZE, 32):
|
|
149
|
+
if self.data[DIR_OFFSET + i] == FREE_BLOCK:
|
|
150
|
+
return i
|
|
151
|
+
return -1
|
|
152
|
+
|
|
153
|
+
def _add_file_entry(self, filename: str, block: int, size_left: int) -> int:
|
|
154
|
+
"""Écrit une entrée de répertoire Thomson (32 octets)."""
|
|
155
|
+
entry = DIR_OFFSET + self._find_free_entry()
|
|
156
|
+
name = Path(filename).stem
|
|
157
|
+
ext = Path(filename).suffix.lstrip('.')
|
|
158
|
+
|
|
159
|
+
# Nom (8 octets, complété par des espaces)
|
|
160
|
+
for i in range(8):
|
|
161
|
+
self.data[entry + i] = ord(name[i]) if i < len(name) else 0x20
|
|
162
|
+
|
|
163
|
+
# Extension (3 octets)
|
|
164
|
+
for i in range(3):
|
|
165
|
+
self.data[entry + 8 + i] = ord(ext[i]) if i < len(ext) else 0x20
|
|
166
|
+
|
|
167
|
+
# Type : 2 = programme assembleur (.BIN, .CHG, .MAP), 0 = BASIC sinon
|
|
168
|
+
upper_ext = Path(filename).suffix.upper()
|
|
169
|
+
file_type = 2 if upper_ext in ('.BIN', '.CHG', '.MAP') else 0
|
|
170
|
+
self.data[entry + 11] = file_type
|
|
171
|
+
|
|
172
|
+
# Type données : 0 = binaire
|
|
173
|
+
self.data[entry + 12] = 0x00
|
|
174
|
+
|
|
175
|
+
# Premier bloc
|
|
176
|
+
self.data[entry + 13] = block
|
|
177
|
+
|
|
178
|
+
# Nombre d'octets dans le dernier secteur (big-endian)
|
|
179
|
+
self.data[entry + 14] = (size_left >> 8) & 0xff
|
|
180
|
+
self.data[entry + 15] = size_left & 0xff
|
|
181
|
+
|
|
182
|
+
# Commentaire (1 octet nul + 7 espaces)
|
|
183
|
+
self.data[entry + 16] = 0x00
|
|
184
|
+
for i in range(1, 8):
|
|
185
|
+
self.data[entry + 16 + i] = 0x20
|
|
186
|
+
|
|
187
|
+
# Date (jour, mois, année sur 2 chiffres)
|
|
188
|
+
t = time.localtime()
|
|
189
|
+
self.data[entry + 24] = t.tm_mday
|
|
190
|
+
self.data[entry + 25] = t.tm_mon
|
|
191
|
+
self.data[entry + 26] = t.tm_year - 2000
|
|
192
|
+
for i in range(3, 8):
|
|
193
|
+
self.data[entry + 24 + i] = 0x00
|
|
194
|
+
|
|
195
|
+
return entry
|
|
196
|
+
|
|
197
|
+
def _write_block(self, block: int, file_bytes: bytes, file_size: int, offset: int):
|
|
198
|
+
"""Écrit jusqu'à 8 secteurs de données dans un bloc."""
|
|
199
|
+
for b in range(8):
|
|
200
|
+
src = offset + b * SECTOR_BYTES
|
|
201
|
+
dst = block * BLOCK_SIZE + b * SECTOR_SIZE
|
|
202
|
+
nb = SECTOR_BYTES
|
|
203
|
+
if src + SECTOR_BYTES > file_size:
|
|
204
|
+
nb = file_size - src
|
|
205
|
+
if nb > 0:
|
|
206
|
+
self.data[dst:dst + nb] = file_bytes[src:src + nb]
|
|
207
|
+
for j in range(nb, SECTOR_SIZE):
|
|
208
|
+
self.data[dst + j] = 0x00
|
|
209
|
+
|
|
210
|
+
def add_file_content(self, filename: str, file_bytes: bytes):
|
|
211
|
+
"""Alloue des blocs et écrit le contenu d'un fichier sur la disquette."""
|
|
212
|
+
size = len(file_bytes)
|
|
213
|
+
blocks = []
|
|
214
|
+
offset = 0
|
|
215
|
+
size_left = size
|
|
216
|
+
|
|
217
|
+
while size_left > 0:
|
|
218
|
+
block = self._find_free_block(blocks)
|
|
219
|
+
self._write_block(block, file_bytes, size, offset)
|
|
220
|
+
offset += BLOCK_BYTES
|
|
221
|
+
|
|
222
|
+
if blocks:
|
|
223
|
+
self.data[FAT_OFFSET + blocks[-1]] = block
|
|
224
|
+
blocks.append(block)
|
|
225
|
+
|
|
226
|
+
if size_left < BLOCK_BYTES:
|
|
227
|
+
# Dernier bloc : calcul du nombre de secteurs occupés
|
|
228
|
+
nb_sectors = 0xc0
|
|
229
|
+
remaining = size_left
|
|
230
|
+
while remaining > 0:
|
|
231
|
+
nb_sectors += 1
|
|
232
|
+
remaining -= SECTOR_BYTES
|
|
233
|
+
self.data[FAT_OFFSET + blocks[-1]] = nb_sectors
|
|
234
|
+
actual_size_left = size_left - SECTOR_BYTES * (nb_sectors - 0xc0 - 1)
|
|
235
|
+
if actual_size_left <= 0:
|
|
236
|
+
actual_size_left = size_left % SECTOR_BYTES
|
|
237
|
+
if actual_size_left == 0:
|
|
238
|
+
actual_size_left = SECTOR_BYTES
|
|
239
|
+
self._add_file_entry(filename, blocks[0], actual_size_left)
|
|
240
|
+
|
|
241
|
+
size_left -= BLOCK_BYTES
|
|
242
|
+
|
|
243
|
+
def add_file(self, filepath: str):
|
|
244
|
+
"""
|
|
245
|
+
Charge un fichier .BIN et l'ajoute à la disquette.
|
|
246
|
+
Ajoute le header/footer Thomson si absent.
|
|
247
|
+
"""
|
|
248
|
+
filename = os.path.basename(filepath)
|
|
249
|
+
with open(filepath, 'rb') as f:
|
|
250
|
+
raw = f.read()
|
|
251
|
+
|
|
252
|
+
size = len(raw)
|
|
253
|
+
file_bytes = bytearray(raw)
|
|
254
|
+
|
|
255
|
+
if filepath.upper().endswith('.BIN'):
|
|
256
|
+
# Vérifie la présence du header Thomson (5 octets) et footer (5 octets)
|
|
257
|
+
if size >= 10:
|
|
258
|
+
size_cont = size - 10
|
|
259
|
+
has_header = (raw[0] == 0x00
|
|
260
|
+
and raw[1] == (size_cont >> 8) & 0xff
|
|
261
|
+
and raw[2] == size_cont & 0xff)
|
|
262
|
+
else:
|
|
263
|
+
has_header = False
|
|
264
|
+
|
|
265
|
+
if not has_header:
|
|
266
|
+
print(f" /!\\ Pas de header Thomson dans {filename}, ajout automatique impossible sans adresse.")
|
|
267
|
+
print(f" Utilisez la syntaxe CMOC qui génère un .BIN avec header.")
|
|
268
|
+
# On laisse passer tel quel (CMOC génère un BIN avec header)
|
|
269
|
+
|
|
270
|
+
print(f" Ajout de {filename} ({size} octets)")
|
|
271
|
+
self.add_file_content(filename, bytes(file_bytes))
|
|
272
|
+
|
|
273
|
+
# ------------------------------------------------------------------
|
|
274
|
+
def add_boot_loader(self, nb_files: int):
|
|
275
|
+
"""
|
|
276
|
+
Écrit le boot loader dans le secteur 1 de la piste 20,
|
|
277
|
+
et les descripteurs de fichiers (adresse, pistes/secteurs) à partir de l'octet 12.
|
|
278
|
+
Équivalent de addBootLoader() dans fdfs.c.
|
|
279
|
+
"""
|
|
280
|
+
boot = BOOTMO_BIN
|
|
281
|
+
size = len(boot)
|
|
282
|
+
|
|
283
|
+
if size > 130:
|
|
284
|
+
raise ValueError(f"BOOTMO.BIN trop grand ({size} octets, max 120 utiles)")
|
|
285
|
+
|
|
286
|
+
# Vérification de l'adresse de boot : doit commencer à $6200 ou $2200
|
|
287
|
+
if not ((boot[0] == 0x00)
|
|
288
|
+
and (boot[3] in (0x62, 0x22))
|
|
289
|
+
and (boot[4] == 0x00)):
|
|
290
|
+
raise ValueError("BOOTMO.BIN invalide : doit commencer à l'adresse $6200 ou $2200")
|
|
291
|
+
|
|
292
|
+
# Écriture du secteur de boot (sector 1, track 0, position 0 dans le .fd)
|
|
293
|
+
# On réinitialise les 256 premiers octets du fichier
|
|
294
|
+
for i in range(SECTOR_SIZE):
|
|
295
|
+
self.data[i] = 0x00
|
|
296
|
+
|
|
297
|
+
checksum = 0x55
|
|
298
|
+
content_size = size - 10 # sans header ni footer
|
|
299
|
+
for i in range(content_size):
|
|
300
|
+
self.data[i] = (256 - boot[i + 5]) & 0xff
|
|
301
|
+
checksum = (checksum + boot[i + 5]) & 0xff
|
|
302
|
+
|
|
303
|
+
# Signature "BASIC2" à l'offset 120
|
|
304
|
+
for i, ch in enumerate(b'BASIC2'):
|
|
305
|
+
self.data[120 + i] = ch
|
|
306
|
+
|
|
307
|
+
checksum = (checksum + 0x6c) & 0xff
|
|
308
|
+
self.data[127] = checksum
|
|
309
|
+
|
|
310
|
+
# Marque le bloc 0 comme réservé dans la FAT
|
|
311
|
+
self.data[FAT_OFFSET] = RESERVED_BLOCK
|
|
312
|
+
|
|
313
|
+
# Construction des descripteurs de fichiers dans la piste 20, à partir de l'octet 12
|
|
314
|
+
n = 12
|
|
315
|
+
base = 20 * TRACK_SIZE
|
|
316
|
+
|
|
317
|
+
for f_idx in range(nb_files):
|
|
318
|
+
entry = f_idx * 32
|
|
319
|
+
|
|
320
|
+
block = self.data[DIR_OFFSET + entry + 13]
|
|
321
|
+
|
|
322
|
+
# Lecture du header Thomson du fichier pour extraire adresse de chargement
|
|
323
|
+
file_addr = (self.data[block * BLOCK_SIZE + 3] << 8) | self.data[block * BLOCK_SIZE + 4]
|
|
324
|
+
file_size = (self.data[block * BLOCK_SIZE + 1] << 8) | self.data[block * BLOCK_SIZE + 2]
|
|
325
|
+
|
|
326
|
+
print(f" Fichier #{f_idx}: addr=${file_addr:04x} taille={file_size} octets")
|
|
327
|
+
|
|
328
|
+
# Adresse de chargement (2 octets)
|
|
329
|
+
self.data[base + n] = (file_addr >> 8) & 0xff
|
|
330
|
+
n += 1
|
|
331
|
+
self.data[base + n] = file_addr & 0xff
|
|
332
|
+
n += 1
|
|
333
|
+
|
|
334
|
+
# Parcours des blocs du fichier
|
|
335
|
+
file_exec = 0
|
|
336
|
+
current_block = block
|
|
337
|
+
src = 0
|
|
338
|
+
|
|
339
|
+
while current_block != FREE_BLOCK:
|
|
340
|
+
next_b = self.data[FAT_OFFSET + current_block]
|
|
341
|
+
nbs = 8
|
|
342
|
+
if next_b > 0xc0:
|
|
343
|
+
nbs = next_b - 0xc0
|
|
344
|
+
next_b = FREE_BLOCK
|
|
345
|
+
# Récupère l'adresse d'exécution dans le footer Thomson
|
|
346
|
+
nb_bytes = (self.data[DIR_OFFSET + entry + 14] << 8) | self.data[DIR_OFFSET + entry + 15]
|
|
347
|
+
src = current_block * BLOCK_SIZE + (nbs - 1) * SECTOR_SIZE + nb_bytes
|
|
348
|
+
if nb_bytes < 2:
|
|
349
|
+
file_exec = self.data[src - 3]
|
|
350
|
+
else:
|
|
351
|
+
file_exec = self.data[src - 2]
|
|
352
|
+
file_exec = (file_exec << 8) | self.data[src - 1]
|
|
353
|
+
print(f" exec=${file_exec:04x}")
|
|
354
|
+
|
|
355
|
+
track = current_block >> 1
|
|
356
|
+
sector = 9 if (current_block & 0x01) else 1
|
|
357
|
+
|
|
358
|
+
self.data[base + n] = track; n += 1
|
|
359
|
+
self.data[base + n] = sector; n += 1
|
|
360
|
+
self.data[base + n] = sector + nbs - 1; n += 1
|
|
361
|
+
|
|
362
|
+
current_block = next_b
|
|
363
|
+
|
|
364
|
+
# Marqueur de fin de fichier + adresse d'exécution
|
|
365
|
+
self.data[base + n] = FREE_BLOCK; n += 1
|
|
366
|
+
self.data[base + n] = (file_exec >> 8) & 0xff; n += 1
|
|
367
|
+
self.data[base + n] = file_exec & 0xff; n += 1
|
|
368
|
+
|
|
369
|
+
# ------------------------------------------------------------------
|
|
370
|
+
def save(self, path: str):
|
|
371
|
+
with open(path, 'wb') as f:
|
|
372
|
+
f.write(self.data)
|
|
373
|
+
print(f"✓ Image .fd écrite : {path} ({DISK_SIZE} octets)")
|
|
374
|
+
|
|
375
|
+
|
|
376
|
+
# ==============================================================================
|
|
377
|
+
# Point d'entrée
|
|
378
|
+
# ==============================================================================
|
|
379
|
+
def main():
|
|
380
|
+
if len(sys.argv) < 3:
|
|
381
|
+
print("Usage: python3 makefd.py output.fd program.BIN [file2.BIN ...]")
|
|
382
|
+
print("")
|
|
383
|
+
print(" Génère une image disquette .fd bootable pour Thomson MO5.")
|
|
384
|
+
print(" Remplace : fdfs -addBL output.fd BOOTMO.BIN program.BIN")
|
|
385
|
+
print(" Le boot loader MO5 est embarqué dans ce script.")
|
|
386
|
+
sys.exit(1)
|
|
387
|
+
|
|
388
|
+
output_fd = sys.argv[1]
|
|
389
|
+
input_bins = sys.argv[2:]
|
|
390
|
+
|
|
391
|
+
print(f"=== Génération de {output_fd} ===")
|
|
392
|
+
disk = FloppyDisk()
|
|
393
|
+
disk.format(output_fd)
|
|
394
|
+
|
|
395
|
+
print("--- Ajout des fichiers ---")
|
|
396
|
+
for bin_path in input_bins:
|
|
397
|
+
disk.add_file(bin_path)
|
|
398
|
+
|
|
399
|
+
print("--- Écriture du boot loader MO5 ---")
|
|
400
|
+
disk.add_boot_loader(len(input_bins))
|
|
401
|
+
|
|
402
|
+
os.makedirs(os.path.dirname(os.path.abspath(output_fd)), exist_ok=True)
|
|
403
|
+
disk.save(output_fd)
|
|
404
|
+
|
|
405
|
+
|
|
406
|
+
if __name__ == '__main__':
|
|
407
|
+
main()
|