CoBra - Sistem de operare CP/M cu 40 coloane text vizibile pe ecran simultan
1. Modul de instalare a sistemului de operare
Pentru instalarea acestei versiuni de CP/M pentru CoBra sînt necesare 2 fișiere CP/M, SYSGEN.COM şi LK.SYS. Fişierul LK.SYS, care conţine CP/M Loader-ul, trebuie copiat pe dischetă imediat după instalarea sistemului propriu-zis în pistele sistem (0 şi 1) ale dischetei. De asemenea mai e necesar și utilitarul PIP, pentru copierea CP/M Loader-ului, și utilitarul FDD.COM, pentru formatarea dischetei, dacă aceasta e neformatată.
Pentru început, un sistem CP/M (evident diferit) va trebui încărcat, pentru a lansa comenzile necesare. Am să descriu procesul de instalare folosind versiunea CP/M cu 80 de caractere pe linie, pe care am împachetat-o în ROM şi se lansează foarte convenabil şi rapid.
Înainte de orice se impune un comentariu foarte important:
Fiecare versiune de CP/M are o cantitate limitată de memorie disponibilă pentru programe utilizator. Pentru a garanta execuția corectă a unui program, trebuie ca dimensiunea fișierului executabil al programului să se încadreze în limita de memorie totală disponibilă a sistemului. În cazul de față, generatorul de sistem SYSGEN.COM (20 KB) este cel mai mare din cele trei fișiere enumerate mai sus, și se încadrează fără probleme în limita de 53 KB de memorie disponibilă în sistemul CP/M cu 80 de caractere pe linie.
Prototipul meu CoBra are o unitate fizică de floppy de 3.5" instalată ca unitate fizică 0 şi un emulator fizic de unitate floppy instalat ca unităţile fizice 2 şi 3 (emulatorul emulează 2 unităţi fizice diferite).
Aşa că am folosit emulatorul pentru a lucra cu unităţile 2 şi 3, am confecţionat o imagine floppy conţinînd fişierele necesare menţionate mai sus şi am folosit-o ca unitate 3 (D:), şi o imagine de dischetă de 720 KB pe care am montat-o ca unitate 2 (C:).
1: | Am lansat sistemul CP/M din ROM cu unitatea implicită D: (conţinînd discheta (imaginea floppy) cu utilitarele de instalare). |
|
2: | Presupunînd că discheta destinație nu este formatată, tastez comanda FDD pentru a lansa utilitarul de formatare dischete. |
|
3: | După un ENTER, ecranul se schimbă și FDD solicită specificarea tipului unității destinație (simplă/dublă față și densitatea exprimată în track per inch). Se alege DS 96tpi, adică [1] |
|
4: | În continuare se cere specificarea numărului de sectoare pe pistă. Se alege 9, corespunzător unei capacități a dischetei de 720 KB, adică [1]. |
|
5: | În continuare se cere să se precizeze dacă e vorba de formatare sau duplicare. Se apasă tasta F pentru formatare. |
|
6: | În continuare se cere precizarea unităţii fizice în care se află discheta care urmează să fie formatată. Se tastează 2. |
|
7: | În continuare se cere introducerea dischetei care urmează să fie formatată. Se apasă ENTER după ce discheta se află în unitatea 2. |
|
8: | Programul întreabă dacă se formatează toată discheta sau doar o parte din ea. Se apasă Y întrucît formatăm tot discul. |
|
9: | În final, se cere confirmarea operaţiei de formatare, după o avertizare asupra pierderii de date. Se apasă ENTER. |
|
10: | După terminarea formatării, programul întreabă dacă se doreşte repetarea operaţiei sau ieşirea înapoi în sistemul de operare. |
|
11: | Se apasă tasta E şi se iese înapoi la promptul CP/M. |
|
12: | Se tastează comanda de lansare a programului de instalare a sistemului de operare în pistele sistem ale dischetei, după care se apasă ENTER. |
|
13: | Ecranul se schimbă și apare un prompt pentru introducerea dischetei destinaţie în unitatea 2 (C:). Adică singura unitate acceptată pentru instalarea sistemului este 2, de unde şi necesitatea montării imaginii floppy destinație ca unitate 2, la începutul procedurii. |
|
14: | Se apasă ENTER cînd discheta e introdusă, și sistemul este copiat în pistele 0 și 1 ale dischetei. După terminare, programul întreabă dacă se dorește repetarea operației. |
|
15: | Se apasă N întrucît nu e nevoie de altă generare de sistem. În acest punct am constatat că dacă încerc să continui și să copiez cu PIP fișierul LK.SYS pentru a finaliza instalarea sistemului, obțin o eroare de sector defect pe discheta destinație. |
|
16: | Singura soluție a fost resetarea înapoi în Boot Manager și reîncărcarea CP/M din SYSTEM ROM. Bănuiesc că SYSGEN corupe cumva sistemul CP/M din RAM. Așa că aici avem iar ecranul de start CP/M cu unitatea implicită D: |
|
17: | Aici am tastat de probă comanda de listare director pe discul destinație. Cînd am făcut asta fără să resetez calculatorul, am obținut o eroare de sector defect pe discul destinație, și discul chiar a rămas cu defect și DUPĂ resetarea calculatorului. |
|
18: | Dar acum, după reset și reîncărcare CP/M, nu mai obțin nici o eroare, ci mesajul care era de așteptat. |
|
19: | În continuare tastez comanda de copiere a fișierului LK.SYS de pe unitatea curentă (D:) pe unitatea C:. |
|
20: | Apăs ENTER și, după terminarea operaţiei de copiere, discheta din unitatea 2 este gata de boot-are cu noul sistem CP/M. |
|
|
|
2. Descrierea sistemului de operare
Acest sistem (în forma lui originală) este rulabil NUMAI de pe unitățile fizice 2 și 3. Mai exact, dacă se folosesc unități de disc dublă densitate, nu vor putea fi folosite cu acest sistem de operare decît ca unități fizice 2 și/sau 3. Unitățile fizice 0 și 1 sînt presupuse a fi simplă densitate, cu alt număr de sectoare pe pistă. Codul din BOOT ROM știe să încarce sistemul CP/M de pe orice unitate fizică (0-3), dar sistemul în sine, odată încărcat, nu va putea accesa discul de pe care a fost lansat decît dacă unitatea fizică respectivă are densitatea corespunzătoare (unitățile 0, 1 SD, unitățile 2, 3 DD).
Încărcarea lui se face din codul standard de BOOT CoBra în configurația hardware de 80 KB RAM, prin apăsarea tastei D.
Secvenţa de pornire a sistemului, aşa cum este afişată pe ecran la apăsarea tastei D din configuraţia de pornire, arată cam în felul următor:
(plimbaţi mouse-ul peste coloana din stînga, de sus în jos şi urmăriţi imaginea mare din dreapta)
Acest sistem pare să fie versiunea originală așa cum a fost creată de echipa CoBra de la ITC Brașov, dacă e să ne luăm după mesajul afișat la pornire. Nu recunoaște nici o tastă ca avînd funcția de BackSpace. Pentru a obține efectul de BackSpace trebuie folosită combinația de taste Ctrl+H. Apăsarea tastei DEL la sfîrșitul unui număr de caractere introduse la promptul CP/M șterge ultimul caracter introdus din buffer-ul consolei, dar nu-l șterge și de pe ecran ci în schimb îl dublează, tipărindu-l din nou. Dacă se mai apasă tasta DEL încă o dată, penultimul caracter este și el șters în același stil, prin retipărirea lui la sfîrșitul liniei de comandă. Practic dacă se continuă apăsarea tastei DEL linia introdusă este reprodusă integral în oglindă, cu toate că ea este de fapt ștearsă din buffer-ul consolei, lucru verificat prin apăsarea tastei ENTER, care nu produce vreun mesaj de eroare cum că s-ar fi tastat vreo comandă nerecunoscută de sistem. Acest comportament își are originea în începuturile calculatoarelor cînd DEL era folosit la ștergerea caracterelor greșite de pe bandă perforată, dar mi se pare a fi o eroare de implementare pe CoBra.
Afișarea pe ecran este organizată în două pagini a cîte 40 de coloane text, care constituie jumătatea dreaptă și cea stîngă a unui ecran text virtual de 24 rînduri x 80 de coloane text. La un moment dat este vizibilă numai una din pagini. Afișarea se poate comuta între cele două jumătăți cu tasta F4.
Pentru determinarea hărții memoriei ocupate de sistem, am început prin a salva conținutul memoriei dintre $0100-$FFFF (cu comanda „SAVE 255 MEMDUMP.BIN”) și a lista conținutul memoriei dintre $0000-$00FF cu utilitarul POWER.
Conținutul primei pagini de memorie listate cu POWER
(snapshot-uri ale celor 2 jumătăți de ecran cu 40 coloane text accesibile cu tasta F4, redînd întregul conținut al ecranului text virtual CP/M de 80 coloane text) |
1: | |
2: | |
Din listingul obținut se vede că adresa de început a modulului BIOS este $F600 (conținutul locațiilor $0001 și $0002, minus 3).
De asemenea, adresa de început a modulului BDOS este $E806 (conținutul locațiilor $0006 și $0007).
Deci memoria liberă disponibilă programelor utilizator (mărimea zonei TPA) ar fi cam $E800-$0100=$E700 adică 57.75 KB |
|
|
0000: C3 03 F6 80 00 C3 06 E8 00 00 00 00 00 00 00 00
0010: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0020: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0030: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0040: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0050: 00 00 00 00 00 00 00 00 00 00 00 00 01 3F 3F 3F
0060: 3F 3F 3F 3F 3F 3F 3F 3F 00 00 00 74 00 20 20 20
0070: 20 20 20 20 20 20 20 20 00 00 00 00 00 00 00 00
0080: 00 4C 4B 20 20 20 20 20 20 53 59 53 00 00 00 09
0090: 02 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00A0: E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 E5
00B0: E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 E5
00C0: E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 E5
00D0: E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 E5
00E0: E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 E5
00F0: E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 E5 E5
|
|
3. Studiul low-level și modificarea sistemului de operare
Am început studiul acestui sistem (prin dezasamblare) în principal cu două scopuri:
- Pentru a-l face rulabil din piste sistem cu format standard (identic cu restul pistelor, 2-79)
- Pentru a-l face să lucreze cu toate cele patru unități fizice posibile (0, 1, 2, 3) pe post de unități DSDD 720K cu 80 piste, 9 sectoare/pistă, 512 octeți/sector
Cu toate că discheta este formatată (cu utilitarul FDD.COM) la început cu același format pe toate pistele (9 sectoare pe o față de pistă, 512 octeți/sector), atunci cînd se rulează programul generator de sistem (SYSGEN.COM), acesta, înainte de a scrie sistemul în pistele 0 și 1, formatează aceste două piste cu un format non-standard, cu cîte un singur sector pe față de pistă, cu 4096 octeți/sector. Pentru a complica lucrurile și mai mult, codul sistemului de operare este scris în pistele sistem în oglindă (de la coadă la cap) pentru ca nu cumva cineva să se poată uita prea ușor la cod și să-l dezasambleze ca să vadă marile „secrete de stat”.
Conținutul pistelor sistem, extras cu software-ul emulatorului HxC |
|
Așa că pentru a scăpa de această „protecție” am decis să transpun sistemul în pistele sistem formatate cu același format ca și restul dischetei. Astfel, o imagine RAW de dischetă sistem ar putea fi manipulată mai ușor cu un eventual utilitar, care să poată trata și codul din pistele sistem la fel cum tratează codul din zona de date a dischetei. În plus, copierea sistemului pe altă dischetă s-ar putea face mult mai ușor, cu utilitarul FDD.COM, prin folosirea opțiunii „Duplicate”, copiind pur și simplu primele 2 piste (0 și 1) și apoi copiind fișierul LK.SYS.
Cum am făcut asta:
Pentru început am generat o imagine RAW floppy goală (formatată) de 720 KB cu numele TEST_SYSG40.img. Asta se poate face în mai multe moduri, unul ar fi cu utilitarul cobra-cpm-diskimg-copy.c scris de mine, altul ar fi cu software-ul emulatorului floppy HxC. Sau, pentru utilizatori Linux înrăiți (ca mine), pur și simplu cu comanda bash:
dd if=/dev/zero bs=1k count=720 | tr '\000' '\345' > TEST_SYSG40.img
întrucît avem 80 piste x 18 sectoare x 512 octeți = 720 KB.
Am convertit apoi imaginea RAW în imagine HFE folosind HxCFloppyEmulator.exe, am pus-o pe un SD Card și în emulator am instalat sistemul pe ea cu procedeul descris la începutul acestei pagini.
Am scos apoi SD Card-ul din emulator și am copiat înapoi în Linux imaginea TEST_SYSG40.hfe. Am încărcat-o în HxCFloppyEmulator.exe:
și am deschis fereastra Track Analyzer selectînd pista 0 fața 0, ca în imaginea de mai jos:
Se poate vedea clar că pe fața 0 a pistei 0 există un singur sector, de 4096 octeți, iar prin punerea cursorului de mouse deasupra zonei verzi (zona sectorului propriu-zis), apare în dreapta jos întreg conținutul sectorului, listat în mod text (hexa) cu tot cu informațiile de formatare aferente. Am selectat cu mouse-ul întregul text, după cum se vede în următoarea imagine, și l-am extras într-un fișier separat, și am repetat operația pentru ambele fețe ale ambelor piste sistem (0 și 1). Am obținut astfel listingul prezentat mai înainte al conținutului pistelor sistem.
Așadar se vede clar că formatul pistelor sistem a fost modificat de către generatorul de sistem. Pentru comparație, în imaginea de mai jos se poate vedea cum arată pista 2 fața 0 (conținînd prima parte a directorului), care are 9 sectoare, numerotate de la 1 la 9, a cîte 512 octeți fiecare:
Apoi am exportat imaginea floppy (cu butonul ”Export” din fereastra principală a HxCFloppyEmulator.exe) în format RAW cu numele TEST_SYSG40.img. În acest format, pistele sistem ocupă zona de început a fișierului de 4 x 4 KB = 16 KB, după care urmează zona de director. Întrucît imaginea dischetei nu conține nimic altceva decît sistemul proaspăt instalat, directorul va conține o singură înregistrare (32 octeți), în prima poziție din director, pentru fișierul LK.SYS copiat după instalarea sistemului în pistele 0 și 1.
Am extras datele din pistele 0 și 1 într-un fișier separat, numit SYSG40_SYS_REV.bin, cu comanda bash:
head -c 16K TEST_SYSG40.img > SYSG40_SYS_REV.bin
Acest fișier are lungimea de 16 KB, adică 2 piste x 2 fețe x 1 sector/față x 4096 octeți/sector.
Folosind utilitarul file-reverse.c scris de mine (prezentat pe pagina „CoBra CP/M - Diverse chestii”), am întors pe dos codul extras și l-am salvat ca SYSG40_SYS.bin, obținînd astfel codul sistem în ordinea corectă:
./file-reverse SYSG40_SYS_REV.bin
mv SYSG40_SYS_REV.bin-rev SYSG40_SYS.bin
Apoi, cu comanda:
dd if=/dev/zero bs=1k count=2 | tr '\000' '\345' > E5fill.bin
am generat un bloc de octeți $E5 de 2 KB (E5fill.bin) pe care apoi l-am atașat la sfîrșitul fișierului SYSG40_SYS.bin, cu comanda de mai jos, obținînd un bloc de 18 KB (SYSG40_SYS_Tracks_full.bin) conținînd codul sistemului urmat de o zonă formatată, care bloc are exact dimensiunea a două piste disc normale pentru acest tip de CP/M (2 piste x 2 fețe x 9 sectoare/față x 512 octeți/sector = 18 KB):
cat SYSG40_SYS.bin E5fill.bin > SYSG40_SYS_Tracks_full.bin
Deci în acest moment am obținut pistele sistem în format normal, cu codul sistemului CP/M cu 40 coloane text vizibile simultan, scris în ordinea normală.
Mai departe, pentru a obține o imagine completă de dischetă sistem bootabilă, este nevoie de generarea zonei de director cu prima intrare alocată CP/M Loader-ului (un nume de fișier cu extensia SYS, cum ar fi LK.SYS, ca să păstrăm denumirea originală), și de atașarea zonei de date (de regulă maxim 512 octeți, adică un sector) a CP/M Loader-ului propriu-zis, urmată de restul întregii zone de date a dischetei.
Pentru început m-am ocupat de problema CP/M Loader-ului în sine (zona lui de date). M-am gîndit la 3 opțiuni:
- Din moment ce originalul folosit de acest sistem (LK.SYS) lucrează cu formatul „protejat” cu sectoare de 4 KB, cea mai simplă opțiune este CP/M Loader-ul de la versiunea de CP/M cu 80 de coloane text vizibile, în forma lui originală care deja lucrează cu același format de pistă cu 9 sectoare.
- Un CP/M Loader adaptat din cel de la versiunea de CP/M cu 80 de coloane text vizibile, dar personalizat cu un logo în stilul celui de la versiunea CP/M CHRIS a lui Pîrvu Cristinel-Dan
- Un CP/M Loader adaptat din originalul folosit de acest sistem (LK.SYS) pentru a funcționa cu piste sistem în format standard. L-am obținut prin aplicarea modificărilor precizate în tabelul aferent, direct asupra fișierului binar original (LK.SYS)
Redau mai jos listingurile în Assembler ale acestor 3 variante.
CP/M Loader pentru CP/M cu 80 de coloane text, CBOT.SYS - codul dezasamblat |
|
CP/M Loader personalizat (cu logo) - listing Assembler | |
|
CP/M Loader versiunea originală CoBra, LK.SYS - codul dezasamblat |
Aceasta e versiunea originală a CP/M Loader-ului folosit cu prima versiune de CP/M CoBra. Este proiectat să funcționeze cu piste sistem formatate cu cîte un singur sector pe o față de pistă, cu 4096 octeți/sector. Imediat după acest listing prezint un tabel cu modificări care adaptează acest CP/M Loader pentru lucrul cu piste sistem cu format standard. Aplicînd aceste modificări direct fișierului binar LK.SYS (cu un editor hexa) se obține foarte elegant un CP/M Loader pentru versiunea CP/M de față (cu 40 coloane text vizibile).
NOTĂ: - Din motive obscure, fișierul LK.SYS păstrat pe dischetele din arhiva mea are dimensiunea de 1152 octeți. Totuși, din listingul dezasamblării de mai jos se vede clar că numai primii 512 octeți conțin codul util de CP/M Loader, restul fiind probabil un rest de cod salvat împreună din greșeală sau poate intenționat pentru a provoca neinițiaților ceva confuzie... Pentru scopul de față am eliminat ce era inutil păstrînd numai primii 512 octeți din acest fișier.
- După cum se vede mai jos, primii 128 octeți par a fi lăsați cu valoarea înscrisă la formatarea dischetei (E5). În mod normal, această zonă liberă ar fi trebuit să înceapă cu o instrucțiune de salt (JP) la începutul zonei de cod propriu-zis (adresa 0080 din acest listing). Din cauză că această instrucțiune de salt lipsește, ce se întîmplă în realitate este că la lansarea CP/M Loader-ului, înainte de execuția codului propriu-zis de CP/M Loader, se execută 128 de instrucțiuni PUSH HL (E5 este codul instrucțiunii PUSH HL) care încarcă zona de stivă cu 256 de octeți în mod inutil. Întrucît la acel moment zona de stivă se află undeva pe la FC00 (la adrese mari) acest lucru nu perturbă îndeplinirea scopului final. Totuși acest ciobănism nu este corect și în versiunea finală de CP/M Loader folosită de mine am adăugat instrucțiunea de salt corespunzătoare la început.
|
|
MODIFICARE LK.SYS PENTRU A FUNCȚIONA CU 9 SECTOARE PE PISTĂ, 512 OCTEȚI/SECTOR |
Locația | Modificare | Comentariu |
$0000 | $E5 -> $00 | primul octet trebuie să fie $00 (NOP), nu $E5 (PUSH HL) |
$0001 | $E5 -> $C3 | opcode pentru JP |
$0002 | $E5 -> $80 | adresa de salt pt. începutul codului (octet inferior) |
$0003 | $E5 -> $00 | adresa de salt pt. începutul codului (octet superior) |
$0097 | $20 -> $24 | $24 x $100 = $2400 = 9 KB = 1 pistă ambele fețe |
$0098 | $FF -> $00 | adresa de start pentru salvare va fi $0000 |
$0099 | $3F -> $00 |
$00AD | $00 -> $01 | comanda MT Read Data să înceapă la sectorul 1 |
$00AE | $00 -> $02 | 512 octeți/sector |
$00AF | $00 -> $09 | EOT, ultimul nr. de sector de pe o pistă |
$00B0 | $00 -> $50 | valoarea GAP3 pentru 9 sect/pistă, 512 octeți/sector |
$01AD | $04 -> $01 | un singur octet de copiat cu LDIR, anume nr. pistă |
$01B1 | $2B -> $06 | înlocuire DEC HL și LD B,(HL) cu LD B,$02 pentru a specifica 512 octeți/sector |
$01B2 | $46 -> $02 |
$01B3 | $2B -> $3E | înlocuire DEC HL și LD A,(HL) cu LD A,$01 pentru a specifica sector nr. 1 |
$01B4 | $7E -> $01 |
$01B5 | $12 -> $00 | ștergere instrucțiune LD (DE),A |
$01C2 | $CB -> $00 | ștergere instrucțiune care schimba opcode pt. INI cu opcode pt. IND |
$01C3 | $DE -> $00 |
$01FC | $23 -> $21 | înlocuire instrucțiuni INC HL și RET cu LD HL,$0000 și RET pentru ca JP (HL) să sară la $0000 după ce ambele piste sistem sînt citite |
$01FD | $C9 -> $00 |
$01FE | $00 -> $00 |
$01FF | $00 -> $C9 |
În afară de codul propriu-zis al CP/M Loader-ului, mai e nevoie de o intrare în director pentru el. Poate fi foarte bine și generat direct și editat cu un editor hexa. Sînt necesari 32 octeți după cum urmează:
octetul 0: | $00 (User Number) |
octeții 1-8: | $4C $4B $20 $20 $20 $20 $20 $20 (Nume fișier, 'LK ') |
octeții 9-11: | $53 $59 $53 (Extensia numelui, 'SYS') |
octetul 12: | $00 (Xl, Nr. extensiei logice de 16KB, octetul inferior al valorii pe 16 biți) |
octetul 13: | $00 (Byte Count, nr. octeți folosiți în ultima înregistrare de 128 octeți. CP/M 2.2 nu suportă acest octet, care este lăsat pe 0) |
octetul 14: | $00 (Xh, Nr. extensiei logice de 16KB, octetul superior al valorii pe 16 biți) |
octetul 15: | $04 (Record Count, nr înregistrări de 128 octeți folosiți de ultima extensie) |
octețul 16: | $02 (octetul inferior al numărului (2) primului bloc alocat pe disc - blocurile 0 și 1 sînt ocupate de directorul CP/M) |
octetul 17: | $00 (octetul superior al numărului primului bloc alocat pe disc) |
octeții 18-31: | $00 (LK.SYS ocupă un singur bloc de 2KB, deci restul de 7 blocuri adresabile de această intrare de director vor avea valoarea $0000) |
În bash o singură linie de comandă rezolvă problema:
echo "00 4C 4B 20 20 20 20 20 20 53 59 53 00 00 00 04 02 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00" | xxd -r -p > CPM_Loader_direntry.bin
Am salvat fișierul astfel generat cu numele CPM_Loader_direntry.bin.
Acesta conține efectiv primii 32 de octeți ai sectorului 1 pista 2 fața 0. Am confecționat astfel prima intrare în director, cu numele LK.SYS (pentru a păstra denumirea standard originală de la CoBra) pe care o voi asocia mai departe cu conținutul CP/M Loader-ului.
Apoi este nevoie de un bloc de octeți $E5 corespunzător restului de sector 1 și sectoarelor 2 3 4 5 6 7, care vor fi goale, adică 512-32 + 6x512 = 3552 octeți. Am generat acest bloc (E5dirfill_1.bin) cu comanda bash:
dd if=/dev/zero bs=1 count=3552 | tr '\000' '\345' > E5dirfill_1.bin
Urmează apoi cei 512 octeți ai CP/M Loader-ului, care poate fi oricare din cele 3 opțiuni de mai sus (sectorul 8 fața 0 pista 2 este primul sector de date de pe disc, deci aici trebuie pus codul Loader-ului).
Apoi urmează alte 10 sectoare goale (sectorul 9 fața 0 pista 2 plus sectoarele 1 2 3 4 5 6 7 8 9 fața 1 pista 2) cu care se completează pista 2.
Și în fine, toate sectoarele (goale) ale pistelor 3-79, adică 77 piste x 20 sectoare.
Deci 10 + 77 x 18 = 1396 sectoare de 512 octeți. Am generat și acest bloc (E5dirfill_2.bin) cu comanda bash:
dd if=/dev/zero bs=512 count=1396 | tr '\000' '\345' > E5dirfill_2.bin
După care am pus cap la cap toate bucățile cu prima opțiune de CP/M Loader:
cat SYSG40_SYS_Tracks_full.bin CPM_Loader_direntry.bin E5dirfill_1.bin CPM_KRYSS_Loader.bin E5dirfill_2.bin > SYSG40_SYSDISK.img
obținînd o imagine RAW de dischetă sistem CP/M (SYSG40_SYSDISK.img) cu 40 coloane text vizibile simultan.
Am convertit această imagine în format HFE și punînd-o pe SD Card, am încercat să încarc sistemul de pe unitatea 2.
A urmat o surpriză neplăcută: după ce border-ul a pîlpîit ca de obicei în albastru și mesajul de pornire CP/M a apărut la marginea de sus a ecranului, în loc să apară ca de obicei prompter-ul CP/M, calculatorul a revenit în BOOT ROM. La început am crezut că poate CP/M Loader-ul mai trebuie modificat cumva. Am încercat și asta dar problema n-a dispărut.
Atunci am început să bănuiesc că poate sistemul în sine mai are vreo „protecție” prin el pentru a împiedica lansarea lui de pe piste sistem formatate standard. Așa că am luat taurul de coarne și m-am apucat să dezasamblez codul CP/M pentru investigarea problemei.
3.1 Identificarea şi dezasamblarea componentelor: CCP, BDOS, BIOS
Punctul de plecare în analiza sistemului de operare CP/M este rutina de încărcare sistem de pe disc aflată în BOOT ROM. Prin analiza ei am aflat cum decurge încărcarea sistemului de pe disc de la zero.
Procesul de încărcare a acestei versiuni de CP/M are următorii paşi:
- La apăsarea tastei D din configuraţia de BOOT, rutina de încărcare CP/M din BOOT ROM caută pe disc un anumit sector care conţine o secvenţă de cod denumită "CP/M Loader". O încarcă în memorie la adresa FC00 şi o lansează în execuţie.
Ca să intru în detalii, algoritmul de localizare a CP/M Loader-ului pe disc este următorul: (se poate afla prin studierea codului dezasamblat de BOOT ROM 80K)
- Se citesc primele 64 înregistrări (intrări, records) din directorul discului în memorie, adică primii 2KB ai directorului;
- În aceste înregistrări se caută primul fișier cu număr utilizator 0 și extensia SYS, unde cei 3 octeți ai extensiei pot avea bitul 7 setat sau nu (0x53, 0x59, 0x53 sau 0xD3, 0xD9, 0xD3). În cazul de față este vorba de fișierul LK.SYS;
- Dacă fișierul este găsit, i se citește prima adresă de bloc alocat pe disc, s-o notăm cu B. LK.SYS are în înregistrarea din director o singură adresă de bloc, care este $0002, deci B=2;
- se calculează o adresă absolută de sector ca fiind Sect# = (B * 8) + 1 = 17;
- Se folosește ca număr de sectoare pe pistă 36;
- Se calculează un număr de pistă ca fiind Trk# = Sect# DIV 36 + 2 = 2 (unde prin DIV am notat operatorul de împărțire cu rezultat întreg);
- Se calculează un număr de sector pe pistă ca fiind Sec# = (Sect# MOD 36 - 1) DIV 4 + 1 = 5;
- Se translatează numărul de sector pe pistă folosind o tabelă de translatare (1 5 9 4 8 3 7 2 6) și se obține Sec# = 8;
- Se citesc de pe disc în memorie la adresa $0000 sectoarele: Sec# (8) de pe pista Trk# (2) fața 0 și Sec#+1 (3) de pe pista Trk#+1 (3) fața 0;
- Se face un salt la adresa $0000 lansîndu-se în execuție codul din sectoarele citite, care se așteaptă să conțină codul de CP/M Loader.
Întrucît fișierul LK.SYS este primul fișier de pe disc, el ocupă primul bloc disponibil pe disc aflat în întregime pe pista 2, care conține:
- sectorul 8 fața 0
- sectorul 1 fața 1
- sectorul 3 fața 1
- sectorul 5 fața 1
Practic sectorul 8 fața 0 de pe pista 2 va conține întreg codul CP/M Loader-ului LK.SYS iar celelalte 3 sectoare vor fi neocupate, întrucît LK.SYS are dimensiunea de numai 512 octeți.
- "CP/M Loader"-ul încarcă de pe disc în memorie toate sectoarele pistelor sistem, în ordinea lor fizică (pista 0 faţa 0, pista 0 faţa 1, pista 1 faţa 0, pista 1 faţa 1). Întrucît (după cum am menționat mai sus) pentru această versiune de CP/M pistele sistem conțin codul sistemului scris în oglindă (de la coadă la cap), sectoarele citite în ordine normală de pe disc sînt salvate în memorie începînd cu adresa 3FFF, în ordine descrescătoare a adreselor, pînă la 0000. După ce toate sectoarele au fost încărcate, se face un salt la adresa 0000, adică se lanseaza în execuţie codul conţinut în ceea ce ar fi trebuit să fie primul sector de pe disc (pista 0 faţa 0 sector 1). Acesta se numeşte "Boot Sector".
- Codul conţinut în "Boot Sector" mută componentele sistemului (CCP, BDOS, BIOS) la adresele la care ele trebuie rulate în mod normal şi apoi face un salt la rutina BOOT din BIOS (la adresa F600), iniţializînd astfel sistemul.
Boot Sector extras din pistele sistem - codul dezasamblat |
NOTĂ:
Întrucît m-am așteptat ca Boot Sector-ul să conțină cod util în toți cei 512 octeți ai unui sector standard, am extras primii 512 octeți din blocul de cod al pistelor sistem (după ce acest bloc a fost în prealabil întors pe dos pentru a-l aduce la ordinea normală, pistele sistem fiind scrise de-a-ndoaselea, după cum am mai menționat). Dar din listingul de mai jos se vede că zona utilă de cod de Boot Sector este destul de mică și este în întregime conținută în primii 256 octeți. De la adresa 0100 deja începe codul modulului BIOS. Deci practic Boot Sector-ul are mărimea de 256 de octeți. |
|
După cum se vede din listingul alătural, codul din Boot Sector, responsabil pentru plasarea efectivă a sistemului în memorie la adresele corespunzătoare, sparge blocul de cod sistem încărcat din pistele 0 și 1 în 6 bucăți separate și execută următoarele lucruri:
- Dezactivează întreruperile și fixează stiva la adresa $0100
- Programează modul de funcționare a interfeței 8255 în mod compatibil Spectrum
- Setează culoarea BORDER albastru și pune semnalul 06 de la portul C al 8255 pe „1” logic pentru a permite accesul CPU la bancul RAM video
- Relochează modulul BIOS în zona $F600-$FFFF;
- Relochează modulul BDOS în zona $E800-$F5FF (imediat sub BIOS);
- Relochează modulul CCP în zona $7800-$7FFF (zona superioară a RAM video). Această zonă este utilizată ca o copie de rezervă a CCP. La lansarea efectivă a sistemului, după terminarea acestui cod de Boot Sector, modulul CCP va mai fi copiat încă o dată în poziția lui finală, adică $E000-$E7FF, imediat sub BDOS. Iar la nevoie, dacă vreun program CP/M tranzitoriu suprascrie zona CCP $E000-$E7FF, modulul CCP va fi recopiat direct din zona copiei de rezervă fără a mai fi nevoie să se acceseze discul. O alternativă elegantă la procedura standard CP/M de readucere a modulului CCP în memorie de pe disc;
- Relochează un bloc de date (generatorul de caractere) pe care l-am denumit BLOCK#1 în zona $6300-$65FF (jumătatea superioară a RAM video)
- Relochează un bloc de cod (conținînd rutine auxiliare BIOS) pe care l-am denumit BLOCK#2 în zona $6600-$6DFF (jumătatea superioară a RAM video)
- Relochează un bloc de cod (conținînd rutine auxiliare BIOS) pe care l-am denumit BLOCK#3 în zona $6E00-$73FF (jumătatea superioară a RAM video)
- Readuce semnalul 06 de la portul C al 8255 pe „1” logic pentru a schimba accesul CPU de la bancul RAM video la bancul DRAM#1
- Execută un salt la adresa $F600, care este adresa de start a funcției BIOS cu numărul 0, BOOT (Cold Boot).
Se mai observă 3 instrucțiuni de încărcare a regiștrilor BC, DE, HL, urmate de două NOP-uri, secvență care pare a fi o fostă rutină de ștergere a zonei de memorie $0100-$E706 din care instrucțiunea LDIR a fost ștearsă prin înlocuire cu doi octeți $00 iar celelalte 3 instrucțiuni au fost lăsate neșterse din lene... Evident secvența este acum inutilă.
|
Deci în acest punct am determinat toate blocurile componente ale sistemului CP/M, care se pot extrage separat și dezasambla pentru studiu amănunțit.
3.2 Investigarea algoritmului ascuns de protecție a formatului pistelor sistem
Pentru investigarea unei eventuale „capcane” ascunse în codul sistem care să împiedice lansarea CP/M din piste sistem cu format standard, trebuie pornit de la punctul de start determinat din studiul Boot Sector-ului. Acolo, după cum am văzut, după relocarea blocurilor de date/cod se face un salt la adresa $F600, care este adresa de start a modulului BIOS, mai precis a funcției numărul 0, BOOT (Cold Start).
Modulul BIOS - codul dezasamblat |
|
După cum se observă din listingul BIOS, la adresa $F600 se află o instrucțiune de salt la adresa $FE2D. Acolo găsim o rutină care mai întîi programează modul de funcționare a Z80-CTC, apoi încarcă regiștrii BC cu $7230, DE cu $F873 și HL cu $F601. Mai departe vedem cum folosind registrul HL ca pointer, se modifică două locații de memorie consecutive, $F601-$F602, care conțin însăși adresa funcției BIOS BOOT pe care o examinăm, cu numărul 0. În loc de $FE2D, acolo se depune valoarea $F873. La adresa $F873 se află o scurtă rutină care provoacă revenirea în codul de BOOT ROM, deci exact manifestarea „sabotoare” care deranjează aici.
Cu alte cuvinte, funcția BIOS BOOT își schimbă singură adresa de start după ce este executată prima dată, comportament dubios de la prima vedere. Să vedem ce descoperim mai departe...
În continuare, alte șmecherii „deștepte” menite să ascundă modul de execuție: este salvată pe stivă valoarea $F603, urmată de valoarea $7230. După care se setează semnalul 06 pe 1 logic, pentru acces CPU la VRAM și se execută un salt la adresa $7071, care se află în bucata de cod denumită BLOCK#3.
Listingul dezasamblării acestui bloc de cod este prezentat mai jos. La adresa $7071 se află o secvență de cod care testează ce unitate fizică este READY și o atribuie ca unitate logică A:, după care execută un RET. Acest RET provoacă un salt la adresa $7230 care după cum am menționat mai sus a fost ultima valoare salvată pe stivă, iar la adresa $7071 s-a ajuns cu o instrucțiune JP și nu CALL.
Zona de cod BLOCK#3 - codul dezasamblat |
|
Deci în continuare ne ducem la adresa $7230 (tot în cadrul BLOCK#3), unde, foarte important, găsim o instrucțiune EXX neurmată de o altă pereche, lucru important de reținut. Apoi se încarcă HL cu adresa de start a mesajului de pornire CP/M și se apelează o rutină care afișează acest mesaj. Următoarea instrucțiune, JP (HL) va executa un salt la adresa conținută în HL după terminarea rutinei de afișare a mesajului CP/M, iar HL, fiind folosit pe post de pointer în timpul afișării mesajului, va conține adresa imediat următoare zonei de memorie care conține mesajul. Dacă ne uităm în BIOS, după mesajul stocat la $FE00-$FE28 se află un octet $00 la adresa $FE29, urmat de instrucțiunea JP $709A. Deci în continuare ne ducem la adresa $709A (tot în cadrul BLOCK#3).
La adresa $709A găsim altă secvență de cod care înlocuiește caracterul '0' din mesajul "Insert disk E in drive 0" cu numărul unității fizice găsite anterior ca READY, după care o mică mișcare de octeți dependentă de tipul unității detectate (DD sau SD):
Dacă unitatea fizică găsită READY este 0 sau 1 (Single Density), la adresele $F659 și $FB4A este depus cuvîntul $F6EA, după care în registrul DE este încărcat cuvîntul aflat la ($F6E8-$F6E9).
Dacă unitatea fizică găsită READY este 2 sau 3 (Double Density), la adresele $F659 și $FB4A este depus cuvîntul $F6CA, după care în registrul DE este încărcat cuvîntul aflat la ($F6C8-$F6C9).
Din listingul BIOS aflăm că valoarea pe 2 octeți stocată la ($F6C8-$F6C9), pentru unitățile 2 sau 3 este $F6D9. Deci acum DE=$F6D9.
Iar în continuare găsim în sfîrșit exact secvența de cod care provoacă beleaua: se trimite o comandă Read ID la unitatea găsită în prealabil ca READY, pe pista 0 fața 0, Această comandă va citi datele de identificare ale unicului sector de pe acea față de pistă. Relevante în acest caz sînt (vezi documentația 8272) R (numărul de sector) și N (octet indicînd mărimea sectorului).
Mai departe în registrul BC se încarcă valoarea compusă RN obținută cu Read ID și se salvează pe stivă. Apoi urmează o instrucțiune EXX care inversează efectul celei menționate anterior și valoarea RN salvată pe stivă se readuce, de data aceasta în registrul HL. După care se scade valoarea în DE din valoarea în HL. Cu alte cuvinte se compară valoarea RN (obținută cu Read ID pentru sectorul de pe pista 0 fața 0) cu valoarea conținută în DE înainte de prima instrucțiune EXX de la adresa $7230.
Să determinăm mai întîi ce valoare conținea registrul DE înainte de prima instrucțiune EXX. Această instrucțiune se află la adresa $7230, la care s-a ajuns prin salt de la a doua secvență de cod executată, anume secvența de la $7071. Dacă examinăm rutina respectivă constatăm că înainte de a sări la adresa $7230, registrul E este încărcat cu octetul $05 (la linia 7093) iar registrul D este încărcat cu octetul de la locația de memorie $FC07, care este $20, după cum se vede din listingul BIOS. Deci valoarea pe 2 octeți conținută în registrul DE înainte de prima instrucțiune EXX este $2005.
Acum să investigăm care ar putea fi rezultatul unei instrucțiuni Read ID executată de 8272 asupra sectorului de 4096 octeți de pe pista 0 fața 0.
Dacă ne uităm în snapshotul anterior al ferestrei Track Analyzer, unde este expusă pista 0 fața 0, vedem în cîmpul text din dreapta jos, deasupra începutului conținutului sectorului respectiv, cîteva rînduri de text cu informații despre structura logică dintre sectoare.
La început se observă expresia „MFM Sector ID 032”, care exprimă faptul că numărul real de sector pentru sectorul de 4096 octeți de pe fața 0 pista 0 este 32 (zecimal), adică $20 (hexa). Deci cu toate că acest sector este unicul de pe fața pistei, el este scris la formatare de către SYSGEN.COM cu numărul de sector 32 în loc de 1 cum ar fi normal. Evident intenționat în combinație cu acest algoritm de „protecție” implementat în BIOS. Deci parametrul R (adică număr sector, vezi documentația 8272) obținut cu comanda 8272 Read ID în rutina $709A este $20. Cît despre N (mărimea sectorului exprimată ca N = log2(bytes/sector) - log2(128)), poate fi de asemenea extras din acel cîmp text, unde mai departe se poate citi „Size ID 0x05”.
Deci dacă formăm un cuvînt de 2 octeți cu valorile R și N avem RN = $2005. Ce să vezi, potriveală mare !!! Exact valoarea din registrul DE înainte de prima instrucțiune EXX !
În concluzie, am dezvăluit algoritmul de protecție ascuns în acest BIOS. Pentru a-l dezactiva, sînt deci necesare 3 modificări:
- La adresa $70CE din zona BLOCK#3 încărcat în memorie, instrucțiunea RET Z trebuie înlocuită cu RET (necondițional, pentru a elimina necesitatea succesului comparației);
- La adresa $FE50 din modulul BIOS încărcat în memorie, instrucțiunea pe un singur octet LD (HL),E trebuie eliminată (prin simplă înlocuire cu un NOP) pentru a lăsa adresa funcției BIOS BOOT nemodificată;
- La adresa $FE52 din modulul BIOS încărcat în memorie, instrucțiunea pe un singur octet LD (HL),D trebuie eliminată (prin simplă înlocuire cu un NOP) pentru a lăsa adresa funcției BIOS BOOT nemodificată.
Mai pe scurt, modificările sînt următoarele:
MODIFICARE COD BIOS CP/M PENTRU ELIMINAREA PROTECȚIEI DE FORMAT PISTĂ SISTEM |
Adresa în CP/M rezident |
Adresa în MODUL.bin |
Adresa în SYSG40_SYS_Tracks_full.bin |
Modificare |
$70CE
$FE50
$FE52 |
$02CE în BLOCK#3.bin
$0850 în BIOS.bin
$0852 în BIOS.bin |
$0DCE
$0950
$0952 |
$C8 -> $C9
$73 -> $00
$72 -> $00 |
În final am modificat cele 3 locații direct în fișierul SYSG40_SYS_Tracks_full.bin, conținînd imaginea completă a celor două piste sistem, pe care am salvat-o ca SYSG40_SYS_Tracks_full_modified.bin.
3.3 Generarea imaginii RAW de dischetă sistem cu pistele 0 și 1 în format standard
Acum că blocul cu codul sistemului propriu-zis este „domesticit”, putem genera o imagine completă, funcțională, de dischetă sistem cu același format pe toate pistele, de la 0 la 79. Revenind la cele 3 opțiuni de CP/M Loader menționate anterior:
1. Pentru o imagine RAW de dischetă sistem CP/M (SYSG40_KR_SYSDISK.img) cu CP/M Loader-ul original al acestei versiuni, am folosit comanda:
cat SYSG40_SYS_Tracks_full_modified.bin CPM_Loader_direntry.bin E5dirfill_1.bin CPM_KRYSS_Loader.bin E5dirfill_2.bin > SYSG40_KR_SYSDISK.img
2. Pentru o imagine RAW de dischetă sistem CP/M (SYSG40_CL_SYSDISK.img) cu CP/M Loader personalizat (logo), am folosit comanda:
cat SYSG40_SYS_Tracks_full_modified.bin CPM_Loader_direntry.bin E5dirfill_1.bin CPM_40c_Loader.bin E5dirfill_2.bin > SYSG40_CL_SYSDISK.img
3. Pentru o imagine RAW de dischetă sistem CP/M (SYSG40_LK_SYSDISK.img) cu CP/M Loader adaptat de la versiunea originală a CP/M CoBra (1989 ITCI Brasov), am folosit comanda:
cat SYSG40_SYS_Tracks_full_modified.bin CPM_Loader_direntry.bin E5dirfill_1.bin LK.SYS_modified.bin E5dirfill_2.bin > SYSG40_LK_SYSDISK.img
3.4 Modificarea sistemului pentru a fi lansat și de pe unitățile fizice 0 și 1 ca unități DD
Toate sistemele CP/M scrise pentru CoBra au fost concepute pe vremea cînd încă mai existau și unități floppy simplă densitate (de regulă de 8"). Ca urmare în codul sistemului a fost rezervată folosirea unităților fizice 0 și 1 pentru unități simplă densitate (SD), și folosirea unităților fizice 2 și 3 pentru unități dublă densitate (DD).
Consecința este că, dacă folosim unități floppy dublă densitate (singurele care mai există în practică), nu putem încărca CP/M de pe ele decît dacă le conectăm ca unitate fizică 2 sau 3. Redau mai jos un snapshot care arată ce se întîmplă atunci cînd încerc să încarc sistemul de pe discheta introdusă în unitatea fizică 0:
|
Ca un mic comentariu, am să menționez și de unde anume vine eroarea din imaginea alăturată:
Cam toate versiunile CP/M pentru CoBra au încorporată în BIOS o rutină care, imediat după lansarea sistemului și afișarea mesajului de început la marginea de sus a ecranului, citește pista 0 fața 0 a discului unde se așteaptă să găsească un sector unic de 4096 octeți, cu numărul de sector 32. După aceea compară mărimea sectorului găsit și numărul lui cu niște valori stocate într-o zonă de date a CP/M. Dacă valorile citite nu corespund cu valorile stocate, se ia decizia de a reseta calculatorul înapoi în configurația de BOOT, lansînd codul din BOOT ROM. Scopul acestei șmecherii este de a împiedica o eventuală hăcuială din partea neaveniților cu scopul de a copia codul sistemului și a-l transpune în piste sistem cu formatare normală.
Interesant este că unele versiuni CP/M au această decizie dezactivată (nu e cazul versiunii de față), dar testul în sine tot este executat. Testul constă efectiv dintr-o instrucțiune Read ID trimisă la 8272, urmată de citirea de la 8272 a octeților cu rezultatul comenzii. Mesajul de eroare din imaginea alăturată exact asta exprimă: eroare la execuția comenzii Read ID, din cauza faptului că densitatea discului (DD) nu corespunde cu densitatea specificată în tabelul DPB atribuit unității respective (SD). Ca urmare citirea datelor de pe disc nu se poate efectua.
|
Întrucît limitarea mi se pare aiurea, am decis să modific codul sistemului pentru a-l convinge să folosească toate cele 4 unități fizice posibile ca unități dublă densitate. Pentru a realiza asta, este nevoie de ceva glagorie în legătură cu funcționarea internă a CP/M. Mai exact și mai pe scurt, pentru fiecare unitate fizică există un Disk Parameter Block (DPB, 15 octeți) și un Disk Parameter Header (DPH, 16 octeți). DPB conține parametrii caracteristici ai unității cum ar fi numărul de sectoare pe pistă și mărimea blocului de alocare pe disc. DPH conține adresa de început a DPB, adresa de început a tabelei de translatare sectoare (XLT), și alte cîteva adrese ale unor zone de memorie folosite de CP/M pentru fiecare unitate fizică în parte.
O teorie ceva mai superficială ar fi că modificarea DPH pentru unitățile 0 și 1 astfel încît să folosească aceleași XLT și DPB ca unitățile 2 și 3 ar trebui să rezolve problema. În practică, pe lîngă asta mai e nevoie și de ceva hăcuială suplimentară a codului din BIOS, pentru a vedea dacă nu cumva mai există și vreo rutină care să manevreze acești parametri în funcție de numărul unității fizice.
Și într-adevăr am găsit și astfel de rutine:
Funcția BIOS SECTRAN (vezi listingul BIOS de mai jos) folosește o discriminare între unitățile 0,1 și 2,3 în calcularea numărului sectorului logic. Pentru scopul de față, ar fi nevoie de ștergerea instrucțiunii JR Z,$F812 de la adresa $F80C prin înlocuire cu două NOP-uri.
Modulul BIOS din CP/M SYSG40 - listingul dezasamblării |
|
Blocul de cod denumit de mine BLOCK#3 (vezi listingul BLOCK#3 de mai jos și de asemenea listingul Boot Sector-ului dezasamblat de și mai jos) folosește două astfel de condiționări în rutinele BIOS READ/WRITE, la adresele $6EA9 (JR Z,$6EAD) și $6EC3 (JR NZ,$6ED4). Pentru scopul de față ar fi nevoie de înlocuirea JR Z,$6EAD cu două NOP-uri și a instrucțiunii JR NZ,$6ED4 cu JR $6ED4.
Modulul BLOCK#3 din CP/M SYSG40 - listingul dezasamblării |
|
Tot în BLOCK#1 mai este o condiționare la adresa $7035 (JR Z,$703F) și încă una la adresa $70AC (JR Z,$70B3), care ambele ar trebui înlocuite și ele cu cîte două NOP-uri,
Sintetizînd toate acestea, am scris un Boot Sector modificat (cu lungimea totală de 256 octeți) care să modifice aceste locații de memorie imediat după despachetarea blocurilor de cod CP/M la adresele lor de lucru și înainte de lansarea efectivă a sistemului. Prezint mai jos listingul Boot Sector-ului original și al acestui Boot Sector modificat.
Boot Sector-ul original din CP/M SYSG40 - listing dezasamblare | Boot Sector-ul modificat pentru CP/M SYSG40 - listing în Assembler |
|
|
Am înlocuit primii 256 octeți (codul sistemului începe după primii 256 octeți, după cum se poate vedea din Boot Sector-ul original) din blocul de cod conținînd pistele sistem cu acest Boot Sector modificat:
tail -c 18176 "SYSG40_SYS_Tracks_full_modified.bin" > DATA.bin
cat Boot_Sector_modified.bin DATA.bin > SYSG40_SYS_Tracks_full_modified_1.bin
rm -fv DATA.bin
și am generat din nou cele trei versiuni de dischetă sistem discutate anterior:
1. Pentru o imagine RAW de dischetă sistem CP/M (SYSG40_KR_SYSDISK.img) cu CP/M Loader-ul modificat al versiunii CP/M cu 80 coloane text, am executat comanda:
cat SYSG40_SYS_Tracks_full_modified_1.bin CPM_Loader_direntry.bin E5dirfill_1.bin CPM_KRYSS_Modified_Loader.bin E5dirfill_2.bin > SYSG40_KR_SYSDISK.img
2. Pentru o imagine RAW de dischetă sistem CP/M (SYSG40_LK_SYSDISK.img) cu CP/M Loader adaptat de la versiunea originală a CP/M CoBra (1989 ITCI Brasov), am executat comanda:
cat SYSG40_SYS_Tracks_full_modified_1.bin CPM_Loader_direntry.bin E5dirfill_1.bin LK.SYS_modified.bin E5dirfill_2.bin > SYSG40_LK_SYSDISK.img
3. Pentru o imagine RAW de dischetă sistem CP/M (SYSG40_CL_SYSDISK.img) cu CP/M Loader personalizat (logo), am executat comanda:
cat SYSG40_SYS_Tracks_full_modified_1.bin CPM_Loader_direntry.bin E5dirfill_1.bin CPM_40c_Loader.bin E5dirfill_2.bin > SYSG40_CL_SYSDISK.img
Și am obținut astfel trei variante de dischetă sistem de pe care CP/M poate fi încărcat cu succes folosind oricare din unitățile 0-3.
În final, două tabele cu link-uri pentru download ale celor trei tipuri de imagini de dischetă sistem menționate mai sus, în format RAW precum și în format HFE utilizabil cu emulatorul floppy HxC:
PRECIZARE IMPORTANTĂ:
Pentru lansarea sistemului CP/M de pe imaginile de dischetă de mai jos NU ESTE NECESAR „Boot-ul CoBra Unificat” scris de mine și prezentat aici la secțiunea „Software / BOOT ROM”.
Aceste imagini de dischetă pot fi foarte bine lansate cu un cod de BOOT original de 2KB (ca pe vremuri). Dealtfel, și dacă se folosește „Boot-ul CoBra Unificat”, pentru lansarea oricărui sistem de pe dischetă, din meniul lui va trebui intrat mai întîi într-unul din codurile BOOT vechi, și apoi din acel BOOT, cu tasta D se încarcă sistem CP/M de pe dischetă. |
IMAGINI DISCHETE SISTEM BOOTABILE - CP/M 40 COLOANE TEXT (cu piste sistem formatate normal, 9 sectoare/pistă) cu sistem încărcabil numai de pe unitățile fizice 2 și 3 |
IMAGINI DISCHETE SISTEM BOOTABILE - CP/M 40 COLOANE TEXT (cu piste sistem formatate normal, 9 sectoare/pistă) cu sistem încărcabil de pe orice unitate fizică (0, 1, 2 sau 3) |