ich bin dabei ein altes ROM zu decrypten und würde dazu gerne im laufenden System die ROM zugriffe einer 4Mhz Z80 CPU sniffen.
Die ROM Zugriffe passieren im System maximal all 1µs (ein memory access kann minimal 4 Taktzyklen lang sein, aber auch erheblich länger).
Ich möchte jetzt folgendes machen:
Pico an den Address und Datenbus anschließen, sowie die RD und IORQ Leitungen (die werden im System benötigt).
Wenn die richtige Bedingung vorliegt (also ROM read und das richtige ROM selektiert ist), dann möchte ich die Daten vom Datenbus lesen (also den Output vom ROM Modul) und in ein 16 KByte Array schreiben.
Dazu muß ich die unteren 14 Bits der Adresse auslesen und die 8 bits vom Datenbus lesen. Das ROM ist immer im Adressbereich C000-FFFF, also muß ich mir die oberen beiden bits nicht merken und komme mit 16Kbyte klar.
Ich habe jetzt über den Raspberry PI pico nachgedacht. Der hat genug GPIOs, ist sehr billig und hat 256kb RAM intern, was ja für meinen Bedarf (Programm + Daten) ausreicht.
Für den Anschluß muß ich noch den Signallevel konvertieren (5 to 3.3V).
Einzige Frage die ich nicht genau beurteilen kann (habe ich keine Erfahrung): Ist der Pico schnell genug mit den 133Mhz um das maximal 1Mhz schnelle read from ROM zuverlässig zu capturen?
Wenn das ROM aktiv ist, muß der PICO nichts weiter tun als auf einen Interrupt auf der RD Leitung zu reagieren und dann Adressleitungen und Datenleitungen zu lesen und das Datenbyte in das Array an der Position der Adressleitungen zu schreiben. Das sollte mit ein paar C Befehlen erledigt sein. Ansonsten befindet sich die CPU im Grunde in einer Warteschleife und wartet auf den nächsten Interrupt, oder gibt gerne auch Irgendwas auf der Console aus etc.
Also, was meint Ihr - ist der PICO schnell genug dafür?
Der Hauptgrund ist, daß das Rom encrypted ist mit etwas das man nicht einfach durch einen decrypter laufen lassen kann.
Es sitzt in einem Modul und hat ein bissl logic zwische dem EPROM und dem Datenbus sitzen.
Die Logic verknüft ~(~M1) AND ((ADR3 XOR DATA5) OR (ADR5 XOR DATA2))
In einfachen Worten:
Immer wenn kein M1 zyklus der CPU vorliegt, dann XORe die Datenbits mit den dazugehörigen Addressbits. Da ich aber rein am ROM code nicht erkennen kann wan ein M1 zyklus vorliegt (OP code read), muß ich das entweder vollkommen disassemblieren und die read routinge des Disassemblers patchen (habe ich schon gemacht) und zu 90% erfolgreicht disassembled, oder eben das echte System den Code ausführen lassen und den Datenbuss nach decryption sniffen.
Das heißt das hier ist der Ansatz zum 2. Punkt.
Ist ein ROM Modul aus den 80er Jahren für den Schneider CPC 464 (Vortex X-Modul).
Leider gibt es den originalen Source code für das ROM nicht, also reengineering. Kleines vollkommen nutzloses Hobbyprojekt :-)
Die fanden sich oft in Arcadeplatinen wie z.B. Pang! und hatten den Opcode decrypted, nicht aber die Daten. Das musste man mühsam durch M1 heraustesten. Die Decryption Tabelle war Akkugestützt flüchtig im Kabuki abgelegt. Akku leer - Spiel tot.
Bei der Herangehensweise findest Du natürlich nur die Datenbereiche, zu denen während der Laufzeit des Programmes zugegriffen wird.
Wenn es aber Codepfade gibt, die nur unter bestimmten Bedingungen aufgerufen werden, fehlen die in Deinem decodierten Ergebnis, solange Du nicht sicher dafür sorgen kannst, daß die entsprechenden Bedingungen erfüllt werden.
Und gerade bei einem Spiel kann ich mir so etwas gut vorstellen, da könnte es Spielelevel geben, die man erst mal erreichen muss, oder besondere "Belohnungsbildschirme", die erst bei Erreichen einer gewissen Punkt- oder Levelanzahl zu sehen sind.
Du könntest das ROM mitsamt seinem Decoder an ein Programmiergerät (o.ä.*) hängen und es einmal komplett mit aktivierter M1-Leitung und einmal komplett mit deaktivierter M1-Leitung auslesen.
*) Selbst stricken: Adress- und Steuerleitungen mit dem Pico erzeugen, resultierende Datenleitungen einlesen. Geschwindigkeit ist hier dann irrelevant, da nicht dem lebenden System auf die Finger geguckt werden muss.
Ah, ja nahe dran. Hier ist es keine CPU die encrypted ist, sondern das ROM selber. Das heißt im ROM sind die Bytes für die Daten encrypted, aber die Opcodes selber nicht. Das heißt man muß einen Spezial-Assembler bauen, der eben die Opcodes unverändert lässt, aber die Daten in verschlüsselter Form ausspuckt.
Die Logik ist hier glücklicherweise in Hardware implementiert, so daß die nicht kaputt gehen kann, mal abgesehen von defekten Bausteinen.
Für die CPU sind die Daten okay (kein Code notwendig), aber das ROM kann man eben so nicht auslesen.
Du könntest das ROM mitsamt seinem Decoder an ein Programmiergerät
(o.ä.*) hängen und es einmal komplett mit aktivierter M1-Leitung und
einmal komplett mit deaktivierter M1-Leitung auslesen.
*) Selbst stricken: Adress- und Steuerleitungen mit dem Pico erzeugen,
resultierende Datenleitungen einlesen. Geschwindigkeit ist hier dann
irrelevant, da nicht dem lebenden System auf die Finger geguckt werden
muss.
Ich glaube das funktioniert nicht, das hatte ich schon mal überlegt.
Nehmen wir mal an M1 ist active high, weil das einfacher zu schreiben ist.
Wenn wir also alles mit aktiviertem M1 auslesen, dann ist das so, als ob wir das ROM direkt auslesen. Es wird also kein decoding gemacht. Und alles wird am Datenbus so ausgegeben, wie es im ROM steht.
Wenn ich allerdings mit deaktiviertem M1 lese, dann wird alles decoded. Ich habe also zwei zu 100% unterschiedliche ROMS. Damit kann ich nicht herausbekommen, was mal ein Opcode war und was nicht.
Habe ich hier einen Denkfehler? Wäre tolle wenn ja :-)
Zum Thema Datenbereiche etc. hast Du vollkommen recht.
Glücklicherweise handelt es sich hier um eine Firmware für einen Floppy Controller. Das meiste ist per RSX-Befehlen direkt anzuspringen und die CPM BIOS calls sind der schwierigere Teil. Aber da ich ja schon zu 90% erfolgreich mit der Dissassemblierungsmethode war, soll dieses Vorgehen quasi nur die Lücke schließen.
Insbesondere sehr schwierig sind reine Datenbereiche, die dann Code enthalten der ins RAM kopiert wird und dort ausgeführt wird. Wenn da irgendwas schief geht, dann fliegt einem das um die Ohren. Das heißt diese Datenbereiche müssen alle decodiert werden, währen code segmente, die einfach nur nicht identifiziert wurden mit dem M1 im Sinn decodiert werden müssen.
Ach so noch was. Natürlich habe ich schon einen decoder in software geschrieben, der die logic auf das ausgelesene ROM anwendet, aber da scheitere ich dann genau daran, daß ich eben nicht weiß (nur auf Basis des ROMs) was ein M1 zyklus (Opcode) ist und nicht decodiert werden müsste und was nur "Daten" für die CPU sind.
ich bin dabei ein altes ROM zu decrypten und würde dazu gerne im
laufenden System die ROM zugriffe einer 4Mhz Z80 CPU sniffen.
Der Z80 unterstützt keine verschlüsselten Speicher. Daher muss auch nichts entschlüsselt werden, sondern liegt im Klartext vor.
Ich möchte jetzt folgendes machen:
Pico an den Address und Datenbus anschließen, sowie die RD und IORQ
Leitungen (die werden im System benötigt).
IORQ hat nichts mit den ROM-Zugriffen zu tun. MREQ zeigt Speicherzugriffe an.
Dazu muß ich die unteren 14 Bits der Adresse auslesen und die 8 bits vom
Datenbus lesen. Das ROM ist immer im Adressbereich C000-FFFF, also muß
ich mir die oberen beiden bits nicht merken und komme mit 16Kbyte klar.
Es bietet sich eher an, das Auslesen durch die /CE- bzw. /CS-Leitung(en) des ROMs zu triggern. Welche dieser Leitungen mit dem Adressdecoder verbunden ist/sind, hängt vom (EP)ROM-Typ und der konkreten Schaltung ab.
Also, was meint Ihr - ist der PICO schnell genug dafür?
Ja, sofern nicht die Nebenfunktionen wie z.B. UART-Übertragung zu lange Interruptsperren o.ä. verwenden.
Der Z80 unterstützt keine verschlüsselten Speicher. Daher muss auch
nichts entschlüsselt werden, sondern liegt im Klartext vor.
Das hat Christoph eigentlich schon ausreichend erklärt.
Zwischen dem ROM und der CPU sitzt eine Logik, die die vom ROM gelieferten Daten "aufbereitet", und zwar in Abhängigkeit des M1-Signales (und zweier Adressleitungen):
Der Z80 unterstützt keine verschlüsselten Speicher. Daher muss auch
nichts entschlüsselt werden, sondern liegt im Klartext vor.
Das hat Christoph eigentlich schon ausreichend erklärt.
Oh, vielleicht hätte ich meine Antwort sofort vollständig verfassen und das Schreiben nicht zwischendurch für längere Zeit unterbrechen sollen... Die ganzen zwischenzeitlichen Nachrichten hatte ich nicht rechtzeitig gelesen.
Einzige Frage die ich nicht genau beurteilen kann (habe ich keine
Erfahrung): Ist der Pico schnell genug mit den 133Mhz um das maximal
1Mhz schnelle read from ROM zuverlässig zu capturen?
Hab's zum Spaß gerade mal in Python (40 verschwenderische Zeilen) auf dem Pico Pi kodiert.
Eine Million Lesezugriffe, Maskierung der relevanten Daten (Adr,data,iorq,rd) und das Abspeichern der opcodes in ein Array brauchen 640074µs.
IORQ hat nichts mit den ROM-Zugriffen zu tun. MREQ zeigt
Speicherzugriffe an.
Das stimmt im allgemeinen, aber im Besonderen brauche ich die IORQ Leitung um den ROM Select abzufangen. Ich darf ja nur bestimmte ROM reads lesen.
Das ist eine Eigenart des Schneider CPC, nicht normalerweise einer Z80 CPU als solchem.
Es bietet sich eher an, das Auslesen durch die /CE- bzw. /CS-Leitung(en)
des ROMs zu triggern. Welche dieser Leitungen mit dem Adressdecoder
verbunden ist/sind, hängt vom (EP)ROM-Typ und der konkreten Schaltung
ab.
Ich wollte es mir einfach machen, und mich einfach an den BUS hängen, also parallel zum ROM. Aber ich kann natürlich einfach die /CE /OE Leitungen vom ROM abfangen und weiß dann genau, wann der konkrete ROM baustein was ausgibt. Dann nur noch die konkreten decodierten Daten abgreifen.
Das ist sicher einfacher ... einfach zwei kleine drähte an den EPROM Sockel anlöten - fertig.
Noch zum Verständnis:
Der CPC hat zwei Teile. Zuerst muß auf dem Erweiterungsboard eine ROM Select Schaltung sein. Diese zieht das /OE auf null, wenn das entsprechende ROM selectiert wurde.
Zusätzlich liefert dann das GateArray ein /ROMEN signal welches mit dem /CE signal des ROMs verbunden wird. Das kann also direkt verwendet werden und ich muß mich nicht om /MREQ und /RD kümmern, da bei /ROMEN durch das GateArray und die logik darin schon sichergestellt ist, daß es ein /RD und ein /MREQ (und noch ein paar interne Register) handelt.
Also diese beiden Signale sind mir daher egal und ich kann mich auf /ROMEN verlassen. IORQ brauche ich nur für ROM-Select und das muß ich auch auswerten. (Einfacher /OE abgreifen am originalen controller, der diese Logic ja schon implementiert hat).
Also, was meint Ihr - ist der PICO schnell genug dafür?
Ja, sofern nicht die Nebenfunktionen wie z.B. UART-Übertragung zu lange
Interruptsperren o.ä. verwenden.
Ich werde das so realisieren, daß ich während des tracings nichts anderes mache (höchstens beim debugging), so daß ich im Grunde nur in einer Warteschleife hänge und auf den nächsten Interrupt warte, aber den Interrupt nicht disable in der Warteschleife etc.
Einzige Frage die ich nicht genau beurteilen kann (habe ich keine
Erfahrung): Ist der Pico schnell genug mit den 133Mhz um das maximal
1Mhz schnelle read from ROM zuverlässig zu capturen?
Hab's zum Spaß gerade mal in Python (40 verschwenderische Zeilen) auf
dem Pico Pi kodiert.
Eine Million Lesezugriffe, Maskierung der relevanten Daten
(Adr,data,iorq,rd) und das Abspeichern der opcodes in ein Array brauchen
640074µs.
Okay, wenn es in Python geht, geht es auch in C :-)
Ich könnte auch ein Arduino Mega nehmen, aber der läuft nur mit 16Mhz. Das hätte den Vorteil, daß er 5V tolerant ist. Dummerweise hat der kein RAM (nur sehr wenig - 8kb reicht nicht).
Also könnte ich mit Arduino evtl. nur die Steurleitungen bearbeiten und ein SRAM direkt an die Datenleitungen und die Adressleitungen hängen und den /WR mit dem Arduino steuern und /CE kommt durch den Abgriff am ROM /OE (also Rom Select). /WR müsste ich dann vom /ROMEN generieren mit etwas delay, damit das ROM auch daten ausgegeben hat und dann nach kurzer Zeit (für das SRAM was mir vorschwebt dann nach 70-100ns wieder wegnehmen. Damit habe ich einen sauberen write und das innerhalb der offiziellen Zyklen, da die CPU ja 500ns braucht von /RD bis wirklich gelesen wird und das EPROM 70ns Antwort Zeit hat. Also nach 70ns liegt das Signal am EPROM ausgang an, und dann brauche ich noch 50ns für das SRAM um die Daten zu schreiben.
Klingt das nach einem Plan? Der Arudino würde dann beim Tracing nur auf einen Eingang hören und einen Ausgang steuern. Das müsste doch mit 16Mhz funktionieren?
Ach ja - wenn das tracing fertig ist, kann ich dann in aller Ruhe das SRAM auslesen und über Serielle Schnittstelle an den PC übertragen.
Ich wollte es mir einfach machen, und mich einfach an den BUS hängen,
also parallel zum ROM.
Direkt am ROM bekommst Du aber nicht die Daten, die Du bekommen willst. Du musst Dich zwischen CPU und "Verwirr-Logik" anklemmen, sonst siehst Du nur das, was im ROM steht (und was Du mit 'nem Programmiergerät auslesen kannst).
Da Dich aber das interessiert, was die CPU sieht, musst Du Dich zwischen dem Busstecker Deines CPC und dem Steckmodul einklinken.
Einen CPC Emulator auf einem Raspi nehmen und das Modul direkt anschließen und direkt während der runtime disassembeln ... also quasi live disassembler durch das was die CPU wirklich sieht. Das könnte in einem Emulator so funktionieren.
Aber ist irgendwie auch nicht mal so eben gemacht.
Einen CPC Emulator auf einem Raspi nehmen und das Modul direkt
anschließen und direkt während der runtime disassembeln ... also quasi
live disassembler durch das was die CPU wirklich sieht. Das könnte in
einem Emulator so funktionieren.
Wozu? Ändert sich doch nichts. Die 16KiB einsammeln, rüber auf den erwachsenen Computer und dann genüsslich auseinander nehmen.
Da fällt mir gerade auf, dann wird das olle Programm ja 41 Zeilen lang… ;-)
Wozu? Ändert sich doch nichts. Die 16KiB einsammeln, rüber auf den
erwachsenen Computer und dann genüsslich auseinander nehmen.
Meine Anmerkungen zur Codeabdeckung hast Du auch gelesen?
Solange das laufende Programm nicht alle Codepfade ausführt, wird nicht der vollständige Inhalt des ROMs gelesen, sondern nur der für den Ablauf des /Teils/ des Programmes.
Wie ich schon schrieb, enthalten Spiele aber auch Code, der nur unter bestimmten Bedingungen ausgeführt wird, z.B. ein spezieller "Belohnungsbildschirm", der nur ausgegeben wird, wenn alle "Level" durchspielt wurden o.ä.
Meine Anmerkungen zur Codeabdeckung hast Du auch gelesen?
Ja selbstverständlich Harald.
Dann lässt man es halt solange laufen bis alles eingelesen ist.
Und wenn's Tage und Wochen dauert. Da muss sich der Spieler schon etwas Mühe geben. ;-)
Dazu muß ich die unteren 14 Bits der Adresse auslesen und die 8 bits vom
Datenbus lesen. Das ROM ist immer im Adressbereich C000-FFFF, also muß
ich mir die oberen beiden bits nicht merken und komme mit 16Kbyte klar.
Das was du eigentlich suchst nennt sich Logic Analyser. Mit denen wird genau sowas gemacht (früher sogar mit Z80 Disassembler...)
Der RP2040 hat zwischen 50kΩ und 80kΩ
Pull-Down.
Da sah das vorhin, als ich es probiert hatte, schon ganz anders aus.
Das ändert rein garnix an der Amplitude des AC-Anteils. Verschiebt die Sache bloß DC-mäßig nach oben. Um das sagen zu können, brauchte ich nichtmal erneut LTSpice anwerfen.
wenn er es denn schafft, nicht jeder Mensch schafft alle Level.
Muss er ja nicht selbst machen.
Für den Gegenwert eines halb-verrotteten Cheeseburgers mit Pommes bekommt man problemlos ein kindlich-schlichtes Gemüt gemietet.
In einem der anderen Threads hier gibt es sogar einen Kandidaten mit einem ganz besonders schlichten Gemüt der sich für solch eine Aufgabe
MAXimal eignen würde.
Verschiebt die
Sache bloß DC-mäßig nach oben. Um das sagen zu können, brauchte ich
nichtmal erneut LTSpice anwerfen.
Und wenn die Amplitude nun plötzlich deutlich über und unter die
Hysterese geht?
Dann reicht das zwar, um ein (näherungsweise symmetrisches) Taktsignal einzulesen, aber nicht, um auch die anderen Signale korrekt zu erfassen, die ja alles andere als symmetrisch sein werden.
Wozu? Ändert sich doch nichts. Die 16KiB einsammeln, rüber auf den
erwachsenen Computer und dann genüsslich auseinander nehmen.
Meine Anmerkungen zur Codeabdeckung hast Du auch gelesen?
Solange das laufende Programm nicht alle Codepfade ausführt, wird nicht
der vollständige Inhalt des ROMs gelesen, sondern nur der für den Ablauf
des /Teils/ des Programmes.
Wie ich schon schrieb, enthalten Spiele aber auch Code, der nur unter
bestimmten Bedingungen ausgeführt wird, z.B. ein spezieller
"Belohnungsbildschirm", der nur ausgegeben wird, wenn alle "Level"
durchspielt wurden o.ä.
Wie schon gesagt, es handelt sich hier um ein ROM für einen Disk-Kontroller - nicht um ein Spiel. Also kann man das mit in menschlichem Ermessen durchspielen, insbesondere da ich nahezu alle Funktionen disassembliert habe und auch weiß, was wie wo passiert, und was die Funktionen so alle machen.
Der CPM Teil macht noch ein paar Probleme - da passiert im Aufruf der BIOS funktionen etwas eigenartiges, was zu Abstürzen führt.
Also ein einfacher Widerstand führt nicht zu einem Ergebnis. Das Signal, das ich abgreifen möchte muß schon akkurat vorliegen, da ich nur 500µs Zeit habe die echten Daten nach anliegen des Signals auszulesen. Es wäre einfach super, wenn es einen PICO oder was ähnliches gäbe was 5V tolerant ist.
Also schnell genug und mit genug IO-Pins und genug RAM um das direkt und ohne irgendwelche zwischenlösungen zu erreichen.
Und damit zerblase ich mir nicht den PICO? Der ist nicht 5V tolerant und die Eingänge vertragen maximal zusammen 50µA ... wenn ich also 24 Eingänge anhänge, dann ist es im Worstcase max 2µA je Eingang, falls alle Eingänge grade High sind.
Auf der anderen Seite haben wir es aber mit NMOS zu tun, die können zum Teil einige mA schieben können. Insbesondere bei dem ROM Modul ist es ja nicht das EPROM, das da schiebt, sondern die TTL Bausteine, die hinter den DatenLeitungen hängen.
Will nur sicher gehen, nicht daß die 5 Euro jetzt ein Wahnsinniger Verlust wären, wenn der PICO über die Wupper geht, aber es wäre mir doch sehr unangenehm, im Sinne ElektroSchrott.
Sicher ist es kein Problem da die Vorwiederstände zwischenzuhängen, insbesondere wenn es nur 2k2 sind.
Allerdings sind jetzt hier im Thread schon zig vorwiederstände vorgeschlagen worden - das macht mich da etwas unsicher.
Wie schon gesagt, es handelt sich hier um ein ROM für einen
Disk-Kontroller - nicht um ein Spiel.
OK, das hatte ich wohl übersehen.
Also kann man das mit in menschlichem Ermessen durchspielen,
Sicher? Das Ding kann z.B. auch Fehlerbehandlung machen, die ohne ein entsprechend fehlerhaftes Medium nicht aufgerufen wird. Oder besondere Arten von Formatierung unterstützen, die ebenfalls nicht immer aufgerufen werden.
insbesondere da ich nahezu alle Funktionen disassembliert habe
Das wiederum macht mich jetzt stutzig. Wie kannst Du (nahezu) alle Funktionen disassembliert haben, wenn Du noch nicht mal den decodierten ROM-Inhalt vorliegen hast?
Also ein einfacher Widerstand führt nicht zu einem Ergebnis. Das Signal,
das ich abgreifen möchte muß schon akkurat vorliegen, da ich nur 500µs
Zeit habe die echten Daten nach anliegen des Signals auszulesen.
Also mit den korrekten Einheiten hast du es wirklich nicht so, oder? Es werden natürlich eher 500ns sein.
Es wäre
einfach super, wenn es einen PICO oder was ähnliches gäbe was 5V
tolerant ist.
Bestmögliche Näherung wäre wohl ein AVR128DA/B/D. Der kann direkt mit 5V betrieben werden, schafft 32MHz Takt, enthält konfigurierbare Logik, die sich sicherlich in deiner Anwendung sinnvoll nutzen ließe und er bietet 16kB SRAM intern.
Die jeweils kleinste Variante mit 28Pins gibt's sogar in DIL zu kaufen.
Was mir außerdem noch zu dem Thema eingefallen ist: du brauchst eigentlich garnicht so viel RAM. Du kannst vorab ja das Ziel-ROM auslesen und dann in zwei Varianten in den Flash des AVR legen, einmal "as is" und einmal, als wären alle Bytes "verschlüsselt". Die genannte Teile haben 128kByte Flash, das sollte also kein Problem sein.
Dann brauchst du im SRAM nur noch eine Bitmap mit einem Bit/Nutzbyte aufbauen, was eben sagt: hole das Byte aus dem "as is"-Image oder aus dem "verschlüsselt"-Image.
Wie schon gesagt, es handelt sich hier um ein ROM für einen
Disk-Kontroller - nicht um ein Spiel.
OK, das hatte ich wohl übersehen.
Also kann man das mit in menschlichem Ermessen durchspielen,
Sicher? Das Ding kann z.B. auch Fehlerbehandlung machen, die ohne ein
entsprechend fehlerhaftes Medium nicht aufgerufen wird. Oder besondere
Arten von Formatierung unterstützen, die ebenfalls nicht immer
aufgerufen werden.
insbesondere da ich nahezu alle Funktionen disassembliert habe
Das wiederum macht mich jetzt stutzig. Wie kannst Du (nahezu) alle
Funktionen disassembliert haben, wenn Du noch nicht mal den decodierten
ROM-Inhalt vorliegen hast?
Ich habe einen Disassembler genommen, der als Source Code vorlag und diesem Beigebracht die Logic beim Lesen aus dem Speicher anzuwenden. Dieser funktioniert auf Basis von graphen und rennt so ab den gegebenen Einsprungadressen alles ab.
Über bleiben die reinen Datenbereiche (z.B. Code, der zur Ausführung ins RAM kopiert wird) und eben Bereiche, die so nicht angesprungen werden. Das sind insbesondere CPM BIOS Calls, die über eine Lookup Tabelle angesprungen werden (eine Basisadresse + ein Wert, der dann zu einem JMP oder Call führt). Und der Wert ist dann auch noch als zwei Bytes hinter dem CALL realisiert, wo dann die angesprungene Routine dann vom Stack die Rücksprungadresse holt und dort die nächsten zwei Bytes ausliest um auf die Adresse zu kommen. Das heißt alles in allem häßlich und fehlerhaft.
Daher will ich genau diese Bereiche Sniffen um den Code direkt zu lesen und nicht zu interpretieren.
Ich habe das ROM also teilweise dekodiert vorliegen (ungefähr 98% correct würde ich sagen), aber das hilft nichts, wenn die CPM Funktionen dann abschmieren, weil ein paar unwesentliche Adressen nicht korrekt sind.
Was mir außerdem noch zu dem Thema eingefallen ist: du brauchst
eigentlich garnicht so viel RAM. Du kannst vorab ja das Ziel-ROM
auslesen und dann in zwei Varianten in den Flash des AVR legen, einmal
"as is" und einmal, als wären alle Bytes "verschlüsselt". Die genannte
Teile haben 128kByte Flash, das sollte also kein Problem sein.
Dann brauchst du im SRAM nur noch eine Bitmap mit einem Bit/Nutzbyte
aufbauen, was eben sagt: hole das Byte aus dem "as is"-Image oder aus
dem "verschlüsselt"-Image.
Das ist eine gute Idee ... ich brauche ja eigentlich nur zu Wissen bei welchen Reads/Adressen M1 zyklen sind.
Also muß ich tatsächlich nur M1 sowie CE/OE vom ROM abgreifen und dann M1 in eine Bitmap schreiben. So kann ich dann auf dem PC nachher beim Decoding die M1 aus einer Tabelle ziehen und so nicht dekodieren und alles andere wird dekodiert.
Ich brauche ja nicht wirklich alle echten Daten aus dem ROM.
Ich habe einen Disassembler genommen, der als Source Code vorlag und
diesem Beigebracht die Logic beim Lesen aus dem Speicher anzuwenden.
Dieser funktioniert auf Basis von graphen und rennt so ab den gegebenen
Einsprungadressen alles ab.
Daher will ich genau diese Bereiche Sniffen um den Code direkt zu lesen
und nicht zu interpretieren.
Ja, das ist verständlich. Nur halte ich es dennoch für optimistisch, daß damit wirklich alle Codepfade gefunden werden; Du müsstest letztlich jede der CPM-BIOS-Funktionen mit allen möglichen Parametern aufrufen und gleichzeitig alle möglichen Fehlerfälle seitens des angeschlossenen Laufwerks durchspielen, um wirklich auf Nummer Sicher zu gehen.
Kannst Du dieses Aufrufen der BIOS-Funktionen nicht auch Deinem Disassembler beibringen? Deren Interface müsste ja dokumentiert sein, also ist klar, welche Parameter zu verwenden sind.
Nur das Simulieren des Laufwerks und dessen potentieller Fehlermeldungen fehlt dann noch.
Wenn Du Deinem Assembler schon das wechselweise Zugreifen auf den "verschlüsselten" und "unverschlüsselten" Speicher beigebracht hast, hast Du auch eine Art Logging implementiert, so daß Du quasi eine Art Bitmap der Speicherzugriffe erstellen kannst (mit drei Zuständen: kein Zugriff, M1-Zugriff, normaler Zugriff)?
Dann könntest Du recht leicht sehen, welche Adressbereiche im ROM noch nie untersucht wurden.
Du meinst, das in Total (egal welche Richtung) nicht mehr als 50mA durch
den PICO fließen dürfen vs. 2x50mA je Richtung?
2x 50mA ist bei den SoC schon mal falsch, maximal 50mA in Summe über die Ports nach + oder -.
Den SoC fehlt die typische Schutzschaltung der AVR Arduino mit Dioden und Widerstand zur Strombegrenzung.
Seit dem ersten Raspi wurde als sicher empfunden die 50mA durch alle Ports zu teilen, damals 50/17 und kam so auf rund 3mA pro Port. Man darf auch nicht die Obergrenzen der VCC und GND Bondings ignorieren denn die GPU zieht ja auch je nach Arbeit ihren Strom darüber und damals war der GPU Strom so maximal 150mA was mit den Ports 50mA den Maxstrom von 200mA ergibt.
Die neueren Taktzahlen sind höher die GPU stärker der Strom wohl höher, siehe Datenblätter wenn verfügbar.
Hmm interessante Aufgabe, evtl hab ich etwas noch nicht ganz verstanden aber das klingt so als könntest du das einfach in software lösen. Zwei dumps, einmal opcodes clear und daten verschlüsselt und einmal anders herum und einer Adresse von der du weißt das sie einen opcode enthällt (hast du ja durch das was du vorher ja schon entschlüsselt hast) dann ist es doch nur noch vergleichen zwischen den dumps und austauschen der verschlüsselten mit den unverschlüsselten daten.
"The 74HC00 is used as RS flipflop to stop the Z80 CPU during I/O operation, giving the needed time to the Atmega32A to interact with the Z80 bus, [...]"
... kann meinen vorherigen Beitrag nicht mehr bearbeiten, war n schnellschuss und sehr wahrscheinlich daneben. Wie funktioniert das mit der verschlüsselung? Wie wird zwischen hier wird jetzt ein opcode gelesen und hier daten die entschlüsselt werden müssen unterschieden?
Ja, das ist verständlich. Nur halte ich es dennoch für optimistisch, daß
damit wirklich alle Codepfade gefunden werden; Du müsstest letztlich
jede der CPM-BIOS-Funktionen mit allen möglichen Parametern aufrufen und
gleichzeitig alle möglichen Fehlerfälle seitens des angeschlossenen
Laufwerks durchspielen, um wirklich auf Nummer Sicher zu gehen.
Das bleibt weiterhin ein Problem, ja - es bleibt kein Einfaches unterfangen. Wobei ich hoffe, daß wenn ich einmal die Einsprungteile korrekt habe (das sind nur wenige Stellen), daß dann auch der Rest passt. Das was direkt als Code im ROM ausgeführt wird ist in der Regel schon richtig.
Problematisch sind so stellen, wo ein Call passiert, der aber nie dorthin zurück kommt (also eigentlich ein JMP) und die nächsten zwei Bytes dann eine Offsetadresse sind. Dann werden diese vom Disassembler als next Opcode gelesen und dann also falsch behandelt.
Kannst Du dieses Aufrufen der BIOS-Funktionen nicht auch Deinem
Disassembler beibringen? Deren Interface müsste ja dokumentiert sein,
also ist klar, welche Parameter zu verwenden sind.
Das kann ich nicht im Disassembler Lösen, da das Problem nicht die BIOS Funktionen sind, sondern die Art und weise wie sie aufgerufen werden. Es werden einfach zwei Bytes nach einem Call falsch interpretiert (müssten dekodiert werden, werden es aber nicht). Das kann ich nur mit einem Emulator und Debugger nachvollziehen und das ist sehr sehr mühsam.
Nur das Simulieren des Laufwerks und dessen potentieller Fehlermeldungen
fehlt dann noch.
Das wäre dann der nächste Schritt.
Dann könntest Du recht leicht sehen, welche Adressbereiche im ROM noch
nie untersucht wurden.
Welche Bereiche noch nicht decoded sind, weiß ich, weil das der Assembler als data bytes (DB) ausgibt - kann man direkt sehen, daß dort kein Diassembling stattgefunden hat.
So kann man leicht Stringtabellen, Jumptabellen und Parameterblocks z.b. für den Diskmonitor finden.
Sobald man weiß was was ist, kann man es dann auch in den Kontext bringen.
Hmm interessante Aufgabe, evtl hab ich etwas noch nicht ganz verstanden
aber das klingt so als könntest du das einfach in software lösen. Zwei
dumps, einmal opcodes clear und daten verschlüsselt und einmal anders
herum und einer Adresse von der du weißt das sie einen opcode enthällt
(hast du ja durch das was du vorher ja schon entschlüsselt hast) dann
ist es doch nur noch vergleichen zwischen den dumps und austauschen der
verschlüsselten mit den unverschlüsselten daten.
Das Problem ist, daß man nicht weiß was OPcodes sind. Wenn ich wüsste was die Opcodes sind und was Daten (also das was hinter dem ersten Bytes des Opcodes kommt, oder reine Daten (Strings etc)), dann wäre es eine äußerst ineffektive Schutzmethode.
Der Schutz ist so simpel und doch so effektiv, daß man sich auch 40 Jahre später noch die Zähne ausbeisst.
... kann meinen vorherigen Beitrag nicht mehr bearbeiten, war n
schnellschuss und sehr wahrscheinlich daneben. Wie funktioniert das mit
der verschlüsselung? Wie wird zwischen hier wird jetzt ein opcode
gelesen und hier daten die entschlüsselt werden müssen unterschieden?
Das hatte ich hier geschrieben.
Es wird in Abhängigkeit des /M1 signals entschieden, was ein OpCode ist und dann nicht durch die Entschlüsselung läuft.
Der Hauptgrund ist, daß das Rom encrypted ist mit etwas das man nicht
einfach durch einen decrypter laufen lassen kann.
Es sitzt in einem Modul und hat ein bissl logic zwische dem EPROM und
dem Datenbus sitzen.
Die Logic verknüft ~(~M1) AND ((ADR3 XOR DATA5) OR (ADR5 XOR DATA2))
In einfachen Worten:
Immer wenn kein M1 zyklus der CPU vorliegt, dann XORe die Datenbits mit
den dazugehörigen Addressbits. Da ich aber rein am ROM code nicht
erkennen kann wan ein M1 zyklus vorliegt (OP code read), muß ich das
entweder vollkommen disassemblieren und die read routinge des
Disassemblers patchen (habe ich schon gemacht) und zu 90% erfolgreicht
disassembled, oder eben das echte System den Code ausführen lassen und
den Datenbuss nach decryption sniffen.
Das heißt das hier ist der Ansatz zum 2. Punkt.
Ist ein ROM Modul aus den 80er Jahren für den Schneider CPC 464 (Vortex
X-Modul).
Leider gibt es den originalen Source code für das ROM nicht, also
reengineering. Kleines vollkommen nutzloses Hobbyprojekt :-)
Den SoC fehlt die typische Schutzschaltung der AVR Arduino mit Dioden
und Widerstand zur Strombegrenzung.
Du verwechselst da was. Es geht nicht um einen der Kleincomputer aus der "Raspberry Pi"-Serie, sondern um einen Microcontroller, der, wenn auf eine Platine gelötet, unglücklicherweise als "Raspberry Pi Pico" bezeichnet wird.
Das aber ist der RP2040, kein SOC, sondern ein normaler und recht bodenständiger Zweikern-ARM.
Übrigens enthalten AVR-Arduinos keinerlei Schutzbeschaltung, die über das hinausgeht, was in AVRs selbst verbaut ist.
Übrigens enthalten AVR-Arduinos keinerlei Schutzbeschaltung, die über
das hinausgeht, was in AVRs selbst verbaut ist.
nicht anderes hatte ich geschrieben, was hast du verstanden, die interne Schutzschaltung wurde ja bei den alten 8-bit AVR x-mal gezeigt. Da funktionierte sogar Rück-Speisung über die Ports solange die Schutzdioden nicht übermäßig gefordert werden. Timer für Canon Cam an der FB Buchse, nicht von mir aber trotzdem genial.
Es wird in Abhängigkeit des /M1 signals entschieden, was ein OpCode ist
und dann nicht durch die Entschlüsselung läuft.
Also Entweder /M1 ist aktiv oder Entschlüsselung.
ah ok, Danke. Ja das ist dann so überhaupt nicht mehr trivial. Das mitschneiden der Daten scheint dann der Beste nächste Schritt zu sein. Zumindest um an mehr Adressen mit opcodes etc. zu kommem um es dem disassembler "einfacher" zu machen.
nicht anderes hatte ich geschrieben, was hast du verstanden
Du hat nicht AVRs, sondern AVR-Arduinos geschrieben. Das erste sind Microcontroller, das zweite Platinen mit Microcontroller und etwas Zusatzbeschaltung drauf.
OK muß ich mal suchen, dachte das ist nur ein weiterer BCM SoC
Nein, das ist ein durchaus interessanter ARM-µC mit 264 kiB SRAM im 48poligen QFN-Gehäuse, zwei Cortex-M0+-Kerne mit 133 MHz, sehr leistungsfähigen mit einer Art Statemachine programmierbaren I/O-Pins ... und ohne Flash-ROM. Das muss extern beigeschnallt werden.
"The 74HC00 is used as RS flipflop to stop the Z80 CPU during I/O
operation, giving the needed time to the Atmega32A to interact with the
Z80 bus, [...]"
Gruss Chregu
Ja, die benutzen den WAIT Pin der CPU um sie zum Warten zu veranlassen. Das Funktioniert auch beim Z80.
Allerdings funktioniert der WAIT erst am Ende eines M-Zyklusses, so daß man die Daten innerhalb des Zyklusses abgegriffen haben muß, um Sie nicht zu verlieren. Die /RD und /MREQ lines werden vorher freigegeben, der Output am ROM also deaktiviert bevor der WAIT beginnt. Man kann also im WAIT selber die Daten nicht mehr lesen.
Ich bau mal ein MiniSetup nuf für die Auswertung von /M1.
Dazu muß ich aber immernoch die Adresse lesen, /M1, sowie /CE und /OE am ROM.
Dann muß der Arduino in der Lage sein innerhalb dieser Zeit zwei Register zu lesen. Das heißt er reagiert auf /CE AND /OE AND /M1 und muß dann zwei Register ASAP lesen, den Adressbus. Dann WAIT ausgeben und den Rest der Logik machen.
Das sollte funktionieren - ist aber schon sehr Zeitkritisch, was den ersten Teil angeht.
nicht anderes hatte ich geschrieben, was hast du verstanden
Du hat nicht AVRs, sondern AVR-Arduinos geschrieben. Das erste sind
Microcontroller, das zweite Platinen mit Microcontroller und etwas
Zusatzbeschaltung drauf.
schade jetzt kackst du wieder Krümel, ich wählte meine Worte mit Bedacht damit es JEDER verstehen kann, du bist offensichtlich immer auf Worte verdrehen geeicht. Ob die alten 8-Bit AVR nackt oder auf Arduino Platinen daherkommen ist doch sowas von schnurzpiepegal!
Der Pico schafft das locker. Es gibt inzwischen ein fertiges Sigrock Modul dafür. Da steht irgendwas von 120MS/s bei 21 Inputs und 50k Samplespeicher. Eine PIO Unit kann theoretisch pro Takt 32 Bit samplen und das per DMA in den RAM schieben.
16kBytes Z80 OPcode aufdröseln sollte kein unüberwindbares Problem sein, zumal es Assembler ist und kein hochoptimierter C-Compiler seine Finger im Spiel hatte.
Ich würde mir zwei ROM-Dumps nehmen (Original und komplett verdreht) und dann mit Notepad und dem Z80 Pocket Guide ans Werk gehen. Am Einsprungpunkt anfangen und dann linear durchgehen. Ein CP/M Bios ist sehr gerade gestrickt und auch die Ansteuerung des Laufwerks ist kein Geheimnis.
Das klingt erst einmal nervtötend, ist aber bestimmt schneller zielführend als erst eine Zusatzhardware aufzubauen und zu entwanzen. Dazu kommt, dass dir anschliessend die Code Analyse auch nicht erspart bleibt.
Dann muß der Arduino in der Lage sein innerhalb dieser Zeit zwei
Register zu lesen. Das heißt er reagiert auf /CE AND /OE AND /M1 und muß
dann zwei Register ASAP lesen, den Adressbus. Dann WAIT ausgeben und den
Rest der Logik machen.
Das sollte funktionieren - ist aber schon sehr Zeitkritisch, was den
ersten Teil angeht.
Deswegen benutzt du lieber meine Empfehlung AVR128Dx. Damit kannst du schonmal die AND-Logik in Hardware abbilden (CCL), brauchst also in der Software nur noch auf ein Signal lauschen. Außérdem hast du den doppelten Takt zur Verfügung, kannst also in einem Z80-Cycle 32 Instruktionen ausführen statt nur 16.
Da sollte genug Power sein, um ohne das WAIT-Geraffel auszukommen. Also ich jedenfalls könnte es damit umsetzen.
Deswegen benutzt du lieber meine Empfehlung AVR128Dx. Damit kannst du
schonmal die AND-Logik in Hardware abbilden (CCL), brauchst also in der
Software nur noch auf ein Signal lauschen. Außérdem hast du den
doppelten Takt zur Verfügung, kannst also in einem Z80-Cycle 32
Instruktionen ausführen statt nur 16.
Da sollte genug Power sein, um ohne das WAIT-Geraffel auszukommen. Also
ich jedenfalls könnte es damit umsetzen.
Habe jetzt mal nach AVR128DB64 boards geguckt. Sieht ja soweit okay aus. Aber was meinst Du mit doppeltem Takt? Laut Datenblatt hat der AVR128DB64 eine max clock von 24Mhz, wie alle AVR128DBs ... Dummerweise haben die immer noch sehr wenig RAM.
Zu mehr als der Bitmap reicht es nicht - wenn ich also das ROM abspeichern möchte, dann brauche ich deutlich mehr Speicher (16KB nur für das ROM).
Irgendwie scheint halt 5V tolerance aus der Mode zu kommen. :-(
16kBytes Z80 OPcode aufdröseln sollte kein unüberwindbares Problem sein,
zumal es Assembler ist und kein hochoptimierter C-Compiler seine Finger
im Spiel hatte.
Ich würde mir zwei ROM-Dumps nehmen (Original und komplett verdreht) und
dann mit Notepad und dem Z80 Pocket Guide ans Werk gehen. Am
Einsprungpunkt anfangen und dann linear durchgehen. Ein CP/M Bios ist
sehr gerade gestrickt und auch die Ansteuerung des Laufwerks ist kein
Geheimnis.
Das klingt erst einmal nervtötend, ist aber bestimmt schneller
zielführend als erst eine Zusatzhardware aufzubauen und zu entwanzen.
Dazu kommt, dass dir anschliessend die Code Analyse auch nicht erspart
bleibt.
Das wäre ja super, wenn es nur CPM wäre.
Das sind 16kbyte ROM randvoll mit allem möglichen.
Der Schneider CPC hat einen BASIC mode, mit einem Teil dafür, einen CPM Mode mit einem BIOS dafür (was zum Teil vom BASIC auch verwendet wird) und dann noch einen kompletten Monitor/Debugger/Disassembler etc.
Einfach mal so eben ist es nicht und da es sehr optimiert werden musste um es eben in die 16Kbyte zu bekommen ist es auch sehr verschachtelt.
Wie schon geschrieben, die Code Analyse ist zu 95% schon durch und das verfahren mit den beiden ROMs führt zu nichts. Ich habe einen Disassembler verändert, so daß er abhängig vom Opcode byte entschlüsselt oder nicht. Das hat schon zu weiten Teilen geklappt. Problematisch sind halt wie auch schon geschrieben die CALLs mit anschließenden zwei Addressbytes. Sowas wie RST#3 beim CPC, der ein FAR Call ist. Direkt hinter dem RST#3 steht eine Adresse, die aber nicht von der CPU selber ausgewertet wird (wie z.B. Call), sondern diese Adresse Zeigt wiederum auf einen sogenannten FAR pointer (Address, Romnummer) und all das wird in der Firmware des CPC dann ausgewertet. Das heißt ein Disassembler weiß das natürlich nicht und würde nach einem RST#3 einfach das nächste Byte als Opcode interpretieren.
Das habe ich soweit im Griff, weil ich ja RSTs direkt entsprechend behandeln kann.
Aber wenn das gleiche mit Calls gemacht wird wo hinter dem 3 byte Call noch eine Addresse steht (also Daten), der Disassembler aber hinter dem Call erstaunlicherweise einen OpCode Erwartet, dann ist das halt mist.
Die Haben halt den Call dazu benutzt um die Addresse hinter dem Call auf dem Stack zu haben, aber tatsächlich handelt es sich dabei um einen JMP auf eien Addresse die mitteles einer Basisadresse und dem Offset hinter dem Call ermittelt wird und dann dort hineingesprungen wird. Der RET führt dann zu der Routine zurück die das Stück mit dem Call aufgerufen hat.
Soweit ist das schon alles klar - und trotzdem ist es unglaublich schwer alle diese Calls zu finden und dann den Disassembler dazu zu überreden, daß nach dem Call nicht weiter Disassembliert wird und vor allem die nächsten zwei Bytes dann databytes sind.
Also alles nicht so ganz so simple. Wenn es einfach wäre, dann hätte das jemand in den letzten 40 Jahren schon lange fertig gemacht.
Wie schon mehrfach geschrieben ist dieses Projekt hier nur um die letzten Reste und klarheiten zu bekommen, also eine weitere Informationsquelle aus dem echten System.
Da braucht man doch kein Board für. Einen AVR128DB28 nagelt man einfach auf Lochraster/Streifenleiter und gut isses. Das ist ja das Geile an den Dingern, die gibt's auch noch in DIL.
Aber was meinst Du mit doppeltem Takt? Laut Datenblatt hat der
AVR128DB64 eine max clock von 24Mhz, wie alle AVR128DBs
Offiziell ja. Tatsächlich laufen sie aber auch mit 32MHz, dann aber nicht über den kompletten spezifizierten Bereich der Umgebungstemperatur. Sprich: so lange du das Ding nur in einer Umgebung mit nicht allzusehr über Zimmertemperatur betreibst, läuft es problemlos. Deutsche Sommertemperaturen (im Schatten) machen jedenfalls noch keinerlei Probleme.
Dummerweise
haben die immer noch sehr wenig RAM.
16kB finde ich schon ganz ordentlich. Wie groß ist denn der ROM eigentlich, um den es geht?
Damit kannst du
schonmal die AND-Logik in Hardware abbilden (CCL)
Habe mich grade mal im Arduino Mega Schlau gemacht.
Ich kann ja direkt ganze Register verwenden. Und glücklicherweise gibt es hier auch genug.
Wenn ich jetzt z.B. den PortC mit den Bits 0-2 auf pullup und input setze, während ich den rest auf output setze, dann kann ich sehr einfach prüfen ob alle drei signale low sind.
if (PINC == 0) {
addressL = PINA
addressh = PINB
data = PINH
set waitpin_high
mach was mit den Daten
set waitpin_low
//bissl warten, damit die Leitungen den neuen Status zeigen.
nop
nop
nop
}
Der kritische Teil sollte in ein paar takten durch sein. Evtl. muss ich sogar nach Erkennen der drei signale noch ein winiziges bischen warten, damit die Signale auch anliegen (ein zwei NOPs).
Offiziell ja. Tatsächlich laufen sie aber auch mit 32MHz, dann aber
nicht über den kompletten spezifizierten Bereich der
Umgebungstemperatur. Sprich: so lange du das Ding nur in einer Umgebung
mit nicht allzusehr über Zimmertemperatur betreibst, läuft es
problemlos. Deutsche Sommertemperaturen (im Schatten) machen jedenfalls
noch keinerlei Probleme.
Dummerweise
haben die immer noch sehr wenig RAM.
16kB finde ich schon ganz ordentlich. Wie groß ist denn der ROM
eigentlich, um den es geht?
Ja, kommt auf den Anwendungsfall an. In meinem Fall mit dem tracing eines 16KByte großen ROMs, muß der Speicher eben 16KB nur dafür haben. Und dann muß ja noch wenigstens etwas Platz für Variablen sein.
Das hier ist halt ein Grenzfall und wenn ich mir da den PICO angucke, dann ist der Super - aber eben wieder nicht 5V tolerant :-(
Der kritische Teil sollte in ein paar takten durch sein. Evtl. muss ich
sogar nach Erkennen der drei signale noch ein winiziges bischen warten,
damit die Signale auch anliegen (ein zwei NOPs).
Igitt. C für zeitkritische Sachen. Dann würde ich doch eher zu der Pico-Lösung raten. Mußt du halt die knapp 4€ ausgeben, die die entsprechende Zahl Level-Shifter kostet. Aber: der eigentliche Trick würde auch dort nicht in C, sondern in Asm passieren. Nur ist es dort halt das Asm der PIOs, nicht AVR-Asm.
Merke: C ist nicht dafür gedacht, exakte Timings zu generieren. Das war nie der Plan.
Der kritische Teil sollte in ein paar takten durch sein. Evtl. muss ich
sogar nach Erkennen der drei signale noch ein winiziges bischen warten,
damit die Signale auch anliegen (ein zwei NOPs).
Igitt. C für zeitkritische Sachen. Dann würde ich doch eher zu der
Pico-Lösung raten. Mußt du halt die knapp 4€ ausgeben, die die
entsprechende Zahl Level-Shifter kostet. Aber: der eigentliche Trick
würde auch dort nicht in C, sondern in Asm passieren. Nur ist es dort
halt das Asm der PIOs, nicht AVR-Asm.
Merke: C ist nicht dafür gedacht, exakte Timings zu generieren. Das war
nie der Plan.
Naja, es sind ja nicht nur die Level Shifter - sondern das ganz muß auch noch funktionieren.
PlanB für einen PICO mit den PIOs wäre, sn74lvc244s zu verwenden und somit 3.3V an die IOs zu legen.
Dann zwei PIO programme ... Programm 1 checked ob die drei Signale anliegen und löst dann interrupt aus.
Programm 2 würde dann nur einen Port mit 22 bit auf einen Rutsch auslesen.
Oder kann ich in einem SM auch auf verschiedene Portgruppen zugreifen?
Ansonsten könnte alternativ noch gehen - check 3 pins (am Anfang) wenn = 0 dann read 25 pins und in FIFO.
Das schöne ist, daß die Reihenfolge der Pins, wie die High gehen immer gleich ist.
Zuerst /CE (ROM_SELECT funktion), und dann /OE.
Wenn ich die Daten komplett speicher, dann muß ich /M1 nicht auswerten, da es mir ja egal ist - allerdings kann ich das separat mitführen, also M1 in einer Bitmap noch extra. Daher einlesen mit dem PIO und dann ab in die FIFO nach 25 bits (autopush oder wie das heißt). Und warten kann ich dann auch gleich direkt die richtige Anzahl an Zeit mit dem Autowait, bevor ich dann die nächste Schleife mache. Und wenn ich den PIO auf 10Mhz takte, dann habe ich 10 PIO Zyklen je Memory Zyklus der CPU. Das heißt ich kann das Warten gut timen.
Um es einfach zu sagen, mir Gefällt der PICO schon besser dafür, hab aber im moment keinen und es ist ein ziemlicher zusätzlicher Verkabelungsaufwand für die vier octal buffer.
Dann zwei PIO programme ... Programm 1 checked ob die drei Signale
anliegen und löst dann interrupt aus.
Programm 2 würde dann nur einen Port mit 22 bit auf einen Rutsch
auslesen.
Die drei Signale extern verknüpfen und das Resultat auf einen JMP Pin für die PIO legen.
Dann sparst du dir das ganze ISR Zeug und lässt die PIO selbst triggern und lesen und das 32Bit Wort in den FIFO spucken. Anschließend warten bis der JMP Pin sich beruhigt (zurück geschaltet) hat und dann geht alles von vorn los.
Den FIFO kann man zu Fuß auslesen (ja, geht auch per ISR oder DMA, aber wozu? Die Aufgabe ist gemütlich und der FIFO ist acht Werte tief!) Daten und Adresse splitten und in ein 16KiB Array schreiben.
Fettich!
Das Ding läuft mit dreistelligen Megahertzen, da kann man sich dann auch gerne die Programmiersprache seiner geringsten Abneigung frei wählen.
Die drei Signale extern verknüpfen und das Resultat auf einen JMP Pin
für die PIO legen.
Das ist nicht nötig. Die PIO ist zwar bezüglich ihrer logischen Fähigkeiten recht eingeschränkt, aber das Verzweigen anhand dreier Signale, die allesamt null sein sollen kann sie schon locker leisten. Also das Äquivalent zu der gezeigten Arduino-"Lösung" des TO.
Der Unterschied wäre halt nur: sie kann das innerhalb zweier PIO-Takte ermitteln, also mit einer Abtastrate von 66.7Mhz (wenn man im spezifizierten Taktbereich bleibt).
Dann sparst du dir das ganze ISR Zeug und lässt die PIO selbst triggern
Genau.
Das Ding läuft mit dreistelligen Megahertzen, da kann man sich dann auch
gerne die Programmiersprache seiner geringsten Abneigung frei wählen.
LOL. Aber wirklich: ganz genau so isses. Bis man dann bei anspruchsvolleren Anwendungen dann doch irgendwann wieder auf Limits stößt. Wenn die PIOs schneller leifern, als die weitere Verarbeitung in der MCU hinterher kommt. Das ist aber im konkreten Anwendungsfall eher nicht zu befüchten.
Dann zwei PIO programme ... Programm 1 checked ob die drei Signale
anliegen und löst dann interrupt aus.
Programm 2 würde dann nur einen Port mit 22 bit auf einen Rutsch
auslesen.
Die drei Signale extern verknüpfen und das Resultat auf einen JMP Pin
für die PIO legen.
Dann sparst du dir das ganze ISR Zeug und lässt die PIO selbst triggern
und lesen und das 32Bit Wort in den FIFO spucken. Anschließend warten
bis der JMP Pin sich beruhigt (zurück geschaltet) hat und dann geht
alles von vorn los.
Den FIFO kann man zu Fuß auslesen (ja, geht auch per ISR oder DMA, aber
wozu? Die Aufgabe ist gemütlich und der FIFO ist acht Werte tief!) Daten
und Adresse splitten und in ein 16KiB Array schreiben.
Fettich!
Das Ding läuft mit dreistelligen Megahertzen, da kann man sich dann auch
gerne die Programmiersprache seiner geringsten Abneigung frei wählen.
Jap, und den externen kann ich mir sparen, weil tatsächlich das /OE bei jedem zyklus neu gesetzt wird. Das heißt es wird vor dem lesen angeschaltet und dann wieder abgeschaltet und für den neuen zyklus wieder angeschaltet.
Das heißt ich kann sowas tun.
loop:
//wait for the ROM to get selected
WAIT 0 PIN 0 ;/CE
//wait for the rom to get enabled
WAIT 0 PIN 1 ;/OE
//now read it into the IN SHIFT register and outpush
IN PINS,25 ;14 Address bits + 8 data bits + 3 control bits
//wait for the rom to get disabled
WAIT 1 PIN 1 ;OE
JMP loop
Da der Pico hier viel schneller ist, kann ich auch keinen read verpassen.
Ich muss vermutlich aber zwischen dem /OE und dem lesen noch was warten, da das ROM ja 70ns benötigt um die Informationen bereitzustellen.
Also evtl. die Frequenz des PIOs doch was drosseln auf 10ns pro takt. dann komme ich recht gut hin mit ein paar delays - in etwas so:
WAIT 0 PIN 1 [8] ;/OE ... das heißt nachdem OE active ist, warte ich 80ns und dann einlesen. ...
Autopush kann ich hier machen - das heißt nach 25 geshifteten bits, einfach automatisch ins FIFO schieben.
Das sollte es eigentlich auf PIO Seite sein.
Konnte btw, nichts finden, wo man mit dem JMP auf PIN 1 high wartet.
Ich konnte nur JMP PIN finden (vordefiniert), aber was passiert wenn der PIN noch 0 ist?
Jede PIO Unit hat vier unabhängige Statemachines. Diese können sich untereinander mit IRQs synchronisieren. D.h. du könntest die Logik auf einer Statemachine implementieren und das Samplen auf einer anderen.
Jede PIO Unit hat vier unabhängige Statemachines. Diese können sich
untereinander mit IRQs synchronisieren. D.h. du könntest die Logik auf
einer Statemachine implementieren und das Samplen auf einer anderen.
Das kann er. Auch in der gleichen SM.
Aber die Befehle für die Logik brauchen Zeit (wenn auch nur wenige ns) und sie addieren sich. Wenn das schnell genug ist…
Jetzt muß ich mal ganz dumm zwischenfragen. Könnt Ihr evtl. über den Busrequest die Herrschaft über den Bus erlangen und alles abfragen, was Euch fehlt?
BUSREQ und Co sind für die Anbindung eines DMA Controllers gedacht. Damit geht die CPU in Lauerstellung und gibt alle Busleitungen frei. Da u.A. auch die M1 Leitung freigegeben wird, passt die ganze Dekodierung dann nicht mehr. Bringt also nichts.
Dann muß der Arduino in der Lage sein innerhalb dieser Zeit zwei
Register zu lesen. Das heißt er reagiert auf /CE AND /OE AND /M1 und muß
dann zwei Register ASAP lesen, den Adressbus. Dann WAIT ausgeben und den
Rest der Logik machen.
Das sollte funktionieren - ist aber schon sehr Zeitkritisch, was den
ersten Teil angeht.
So ähnlich hatte ich das mal bei einem 1,7 MHz-Z80-System probiert (nur sniffen). Das ging schon mit einem STM32 nicht wirklich gut. Obwohl man dort die Adressen und die Daten+Steuerleitungen mit nur jeweils einem Portzugriff einlesen kann.
Letztendlich habe ich nur noch in einer Schleife die Bussignale gesampelt und einen Puffer gefüllt. Damit geht aber nur noch Offline-Analyse.
Meine Intention war es das Ganze mit Zugriffen auf bestimmte Speicher- oder IO-Adressen zu triggern.
Heute würde ich mir daher den RP2040 näher anschauen.
Könnt Ihr evtl. über den Busrequest die Herrschaft über den
Bus erlangen und alles abfragen, was Euch fehlt?
Das würde nicht helfen. Der fiese Trick dieses Controllers besteht darin, daß der laufende Code selbst entscheidet, welche Bytes "verschlüsselt" werden - nämlich nur die, die Opcodes enthalten (d.h. bei aktivem M1 gelesen werden). Alle anderen Bytes aber sind nicht "verschlüsselt", d.h. auch /Parameter/ für Opcodes.
Ein Beispiel:
SUB 0x22
LD H,0x92
In "unverschlüselt" ist das die Bytefolge
0xd6 0x22
0x26 0x92
Die Bytes 0xd6 und 0x26 sind die Opcodes, die Bytes 0x22 und 0x92 aber sind die "Parameter" der Instruktionen.
0xd6 und 0x26 werden von der Rom-Logik "verschlüsselt", die beiden anderen Bytes aber nicht.
Wenn Du nun mit einem Busrequest die CPU anhälts, und den ROM-Adressbereich ausliest, dann erhältst Du die "Parameter" in korrekter Form, aber falsche Opcodes, denn Deine Lesefunktion weiß nicht, welches Byte sie als Opcode und welches Byte sie als Daten zu lesen hat.
Und damit ist man keinen Schritt weiter als wenn man das ROM mit einem Programmiergerät ausliest (oder auf dem cpc464 ein kleines Programm schreibt, das einen Hexdump des interessanten Speicherbereichs ausgibt).
Ein bissel Frage ich mich ja auch, welchen Weg die Entwickler genutzt haben, um den Code zu verschlüsseln.
Da muß ja fast ein modifizierter Assembler zum Einsatz gekommen sein...
Da muß ja fast ein modifizierter Assembler zum Einsatz gekommen sein.
Damals (TM) gab es den Assembler im Quellcode dazu. Da war es eine Kleinigkeit, solche "Schikanen" einzubauen. Beliebt war auch, Datenleitungen zu tauschen oder der Sprung auf das zweite Byte einer Instruktion. Einen "normalen" User konnte man damit etwas verwirren, Profis haben nur müde gegrinst.
loop:
//wait for the ROM to get selected
WAIT 0 PIN 0 ;/CE
//wait for the rom to get enabled
WAIT 0 PIN 1 ;/OE
//now read it into the IN SHIFT register and outpush
IN PINS,25 ;14 Address bits + 8 data bits + 3 control bits
//wait for the rom to get disabled
WAIT 1 PIN 1 ;OE
JMP loop
Also das würde ich definitiv anders machen. Aber: mir fehlt hier irgendwie das alles entscheidende M1-Signal und auch die genaue Aufgabe des Codes.
Der Code sieht eher so aus, als willst du erst ein ROM-Image aufbauen. Wie weiter oben schon erwähnt, ist das aber überhaupt nicht nötig, weil du es vorab bereits gewinnen und in den zwei Varianten bereitstellen kannst.
Es kann also sinnvollerweise nur noch um die Gewinnung der Bitmap gehen. Und dazu brauchen die Daten überhaupt nicht eingelesen werden, nur die Adresse. Und das auch nur dann, wenn halt M1 Low ist.
Der MCU-Code muß dann bloß noch die von der PIO gelieferten Adressen benutzen, um in der Bitmap jeweils eine 1 zu setzen.
Ich vermute darüber hinaus, dass die Sache insgesamt sowieso nicht ganz so einfach ist. Wenn ich so einen Kopierschutz bauen würde, würde ich natürlich auch dafür sorgen, dass es keine eindeutige Abbildungsfunktion geben kann. D.h.: ein und dasselbe Byte im ROM mal als Opcode und mal als Datum verwendet wird. Es reichen ja eine Handvoll Bytes, verteilt über strategisch wichtige Stellen...
Wenn ich so einen Kopierschutz bauen würde, würde ich
natürlich auch dafür sorgen, dass es keine eindeutige Abbildungsfunktion
geben kann. D.h.: ein und dasselbe Byte im ROM mal als Opcode und mal
als Datum verwendet wird.
Das ließe sich aber mit einer Laufzeitbeobachtung herausfinden. Wie Du schon sagtest, alle Lesezugriffe auf den ROM-Adressbereich erfassen, und den Zustand von M1 dazu, um eine Bitmap zu erhalten.
Die Bitmap sollte zwei Bits pro Adresse enthalten, eines, das gesetzt wird, wenn mit aktivem M1 zugegriffen wird, und eines, das gesetzt wird, wenn mit inaktivem M1 zugegriffen wird.
Lässt man das ausreichend lange laufen (um alle Codepfade zu befriedigen), sollten sich derartige "Fiesheiten" leicht feststellen lassen.
Mal ein anderer, primitiverer Ansatz: Entscheidend ist doch, was die CPU sieht. Also brauchen wir den Adressbus und den Datenbus direkt an der CPU. Der Abtastzeitpunkt wird durch /MEMRQ und /RD und /CE-Eprom bestimmt, Feiglinge warten noch 20ns, bevor sie die Daten lesen.
Zum Abspeichern wird ein Array passender Größe mit 0x00 initialisiert und die gelesenen Daten werden stumpf an der jeweiligen Adresse abgespeichert.
Nachdem einige Zeit mit dem Rechner gespielt wurde, wird das Array ausgelesen und kann mit einem normalen Disasm behandelt werden. Natürlich bleiben Lücken. Da muss man dann bei Bedarf händisch heran gehen.
Der Hardware und Software Aufwand sind drastisch geringer.
Noch eine Anmerkung: Ein typisches CP/M Bios hat etwa 2kBytes Größe. Der Aufbau ist bestens dokumentiert. Damit würde ich anfangen.
Mal ein anderer, primitiverer Ansatz: Entscheidend ist doch, was die CPU
sieht. Also brauchen wir den Adressbus und den Datenbus direkt an der
CPU.
Reicht nicht, aus genau dem Grund, den "observer" angesprochen hat. Es ist nicht ausgeschlossen, daß /ein und dieselbe Adresse/ mal als Code und mal als Daten angesprochen wird, d.h. mit aktivem und mit inaktivem M1.
Das würde bei Deinem Vorgehen nicht erfasst werden können.
Damit wäre der Wiederstand dann für 2mA pro Line x24 bei 5V auf 2500Ohm
zu setzen. 2k7 sollte also gut passen.
Abgesehen vom "Wiehern" frage ich mich, wie Du auf 2 mA kommmst. Bei 3,3 V des RP2040, (maximal) 5 V der Z80 Logik und 0,5 V Durchlassspannung der Eingangsdioden müssen ca. 1,2 V (5 - 3,8) Spannungsabfall berücksichtigt werden. Bei 2,2 kOhm fließen da rund 0,5 mA.
Die NMOS Mikroprozessoren waren noch auf TTL-Pegel ausgelegt, sodaß bei geringer Ausgangsbelastung, der '1'-Pegel schon garkeine 5 V mehr erreichen konnte.
Jede PIO Unit hat vier unabhängige Statemachines. Diese können sich
untereinander mit IRQs synchronisieren. D.h. du könntest die Logik auf
einer Statemachine implementieren und das Samplen auf einer anderen.
Das kann er. Auch in der gleichen SM.
Aber die Befehle für die Logik brauchen Zeit (wenn auch nur wenige ns)
und sie addieren sich. Wenn das schnell genug ist…
Das wird es sicherlich sein. Normale EPROMs hatten seinerzeit 450 ns und extravagante 250 ns Zugriffszeit. Das könnte man vielleicht noch in einer Hochsprache erledigen - mit PIO sicherlich kein Probelm.
Die Bitmap sollte zwei Bits pro Adresse enthalten, eines, das gesetzt
wird, wenn mit aktivem M1 zugegriffen wird, und eines, das gesetzt wird,
wenn mit inaktivem M1 zugegriffen wird.
Lässt man das ausreichend lange laufen (um alle Codepfade zu
befriedigen), sollten sich derartige "Fiesheiten" leicht feststellen
lassen.
Ja. Die Crux ist halt die Unwägbarkeit, ob wirklich alle möglichen Codepfade bereits durchlaufen wurden. Das wurde ja schon viel früher in diesem Thread erwähnt.
Der eigentliche Punkt ist aber: Wenn der Kopierschutz diesen Mechanismus benutzt, kann es eben kein ROM geben, was ihn aushebelt. Dann ist die einzige Möglichkeit ein funktionaler Nachbau des "Decoders".
Aber halt, neue Idee, viel einfacher: Man nimmt einfach zwei echte ROMs. Eines enthält die "as is"-Variante, das andere die vollständig verschlüsselte. Dann braucht man nur noch mittels M1 zu steuern, an welches der beiden ROMs das OE für den aktuellen Cycle geht.
PiPico:
Ein 16KiB array mit ff füllen.
Im laufenden System bei jedem Zugriff den M1 status für jede genutzte Adresse als 00|01 speichern.
Regelmäßig (alle paar paar Sekunden) das Array zB. über DMA->UART übertragen. Braucht keinerlei Rechenzeit.
Auf dem PC eine simple Anzeige des kompletten Speicherbereiches als Bitmap Grafik anzeigen.
ff (nicht besucht) ->schwarz
00 (Daten) ->grün
01 (Opcode) ->rot
Da kann man dem Zeug beim Arbeiten sogar zuschauen. Der gespeicherte M1 Status hilft dann beim Disassemblieren bzw. dekodieren gemäß Vorgabe.
Das ganze leicht anpassen für doppelte Nutzung Opcodes/Daten.
nicht ausgeschlossen, daß ein und dieselbe Adresse mal als Code
und mal als Daten angesprochen wird
Das wäre mir völlig egal. Der Code muss ohnehin analysiert werden. Dann merkt man das sofort und kann diese Ecke händisch genauer überprüfen. Das Erzeugen des Disasm Listings ist 10% der Wegstrecke. Die richtige Arbeit beginnt erst danach.
nicht nur die, auch die Adressleitungen, der Tausch ist ja statisch und gilt immer und ist am ausgelöteten ROM oder EPROM nicht zu erkennen.
(Ich erinnere mich ganz dunkel kann die Quelle aber nicht mehr benennen)
Ich Antworte hier mal auf eine Sammlung von Punkten, die durch verschiedene Autoren eingebracht wurde. Ich kann aber nicht jeden einzelnen hier Sinnvoll zitieren, da ich nicht threads für Zitate selektieren kann.
Zum Thema mehrere PIOs/SMs und IRQs.
Ja das könnte man machen, ist hier aber nicht notwendig und verkompliziert den Code. Ist außerdem nicht schneller.
Hier ist es ja so, daß die technische Umsetzung in der lebenden Maschine zwei Dinge erfordert.
Selectieren eines ROMs
Dann aktivieren des Roms mit einer bestimmten Adresse
Dann ausgabe des Inhaltes der Adresse durch das ROM.
Schritt 1 passiert nicht ständig und benötigt zwischen diesen Schritt und der ersten tatsächlichen Verwendung des ROMs in der Firmware des Computers noch ein paar weitere Z80 Befehle. Das heißt es vergehen einige µs dazwischen.
Der PIO kann aber mit einer Taktfrequenz von 125Mhz und damit 8ns arbeiten.
Weiterhin liegen zwischen Schritt 2 und 3 mindestens die Antwortzeit des ROM (240ns typisch, aber bei dem von mir geplanten FLASH Eprom sogar nur 70ns). Die CPU selber wartet aber 500ns bevor sie tatsächlich die Daten liest um eben genau dem ROM die notwendige Zeit zu geben.
Das Zeitfenster in dem ich also die Daten gelesen haben muß ist vom Moment /CE bis 500ns später, wenn die CPU das signal irgendwann wieder wegnimmt.
Das ist genau mit der von mir beschriebenen Methode problemlos möglich - ich muss sogar aufpassen, daß ich lange genug warte bevor ich versuche zu lesen.
BUSREQ:
Könnt Ihr evtl. über den Busrequest die Herrschaft über den
Bus erlangen und alles abfragen, was Euch fehlt?
Kann nicht verwendet werden, da die CPU erst im nächsten Fetch zyklus (e.g. M1) darauf reagiert. Das heißt ein Opcode wird erst komplett zu Ende gelesen und bearbeitet, bevor die CPU dann über BUSACK dem sender signalisiert, daß es alle control, address und datenleitung in high impedanz gesetzt hat.
Harald K. schrieb warum einfach nur lesen nichts hilft
Danke Harald, es ist im Grunde so wie Du schreibst - in der realen Hardware genau umgekehrt.
M1 Zyklen führen zu unveränderten Ausgabe des ROM bytes
alle Parameter oder Daten werden decrypted.
Rick fragte, wie die Verschlüsselt wurden
Ein bissel Frage ich mich ja auch, welchen Weg die Entwickler genutzt
haben, um den Code zu verschlüsseln.
Da muß ja fast ein modifizierter Assembler zum Einsatz gekommen sein...
Da habe ich auch lange drüber nachgedacht, aber es ist einfach der Assembler. Es wurden grundsätzlich 2 Pass Assembler verwendet und jeder Assembler weiß ja was Opcode und was Parameter oder Daten sind.
Alles was nicht Opcode ist, wird halt verschlüsselt und die notwendige Addresse dazu weiß man im 2. Pass ja (man weiß genau wo man ist im Code).
Ist bei der Erzeugung also recht einfach zu erreichen indem man den Assembler eben dieses Encoding bybringt. Das sind zwei einfache XORs von zwei bits auf basis der Adresse (bits)
Zum Thema Motivation.
Also das würde ich definitiv anders machen. Aber: mir fehlt hier
irgendwie das alles entscheidende M1-Signal und auch die genaue Aufgabe
des Codes.
Wenn ich nur eine Hardware bauen wollte, die das reproduziert, dann würde ich einfach ein PAL nehmen und fertig. Der ganze Aufwand der Dekodierung macht keinen Sinn, wenn man einfach die Hardware (heute) sehr simpel nachbauen kann. Man könnte auch Alles (inklusive ROM) in einem FPGA oder CPLD unterbringen und das noch simpler gestalten. Oder man kann einen µController nehmen der das dann tut.
Aber das ist nicht die Intention. Die Intention ist es den Quelltext vollständig zu haben. Das heißt es muß final dekodiert werden. Entweder auf dem Weg des Mitschreibens, oder auf dem Weg der Erlangung der dafür notwendigen Informationen (M1) um es offline machen zu können.
Der Weg meines PIO scriptes ist wie folgt:
Ich prüfe die notwendigen Bedingungen ob ein ROM zugriff erfolgt (e.g. /OE /CE) und capture dann mit dem PIO alles, was für die weitere Auswertunge in dern ARM cores notwendig ist: Das sind M1, Address und Datenleitungen. Theoretisch brauche ich auch M1 nicht, aber ich möchte eben die Bitmap für M1 mitlesen. Ob ich noch mittracken will, wenn eine Zelle gar nicht getroffen wurde (weiße Flecken) muss ich mir noch überlegen, dafür hat der PICO aber definitv genug RAM um das auch noch zu machen.
Natürlich muß dann der ARM das Datenbyte in ein Array schreiben und M1 in eine Bitmap und ggfs. noch eine zweite Bitmap für das mapping (gelesen oder nicht gelesen).
Eindeutige Abbildungsfunktion.
Wenn ich so einen Kopierschutz bauen würde, würde ich
natürlich auch dafür sorgen, dass es keine eindeutige Abbildungsfunktion
geben kann. D.h.: ein und dasselbe Byte im ROM mal als Opcode und mal
als Datum verwendet wird.
Das ist sicher eine sehr gute Idee, funktioniert aber mit diesem verwendeten in Hardware implementierten Decoder nicht. Der verwendete Algorithmus ist auch so schon unmöglich zu hacken - ohne das komplette ROM mitzutracen, wie wir hier grade besprechen. Das war sicher damals eher unmöglich, bzw. hat 16kb SRAM und ein bisschen logic benötigt.
Das wäre in den 1980 definitiv ausserhalb der Reichweite von Leuten wie mir gewesen, der das privat mal eben cracken will um es dann zu vermarkten.
von Georg G.
Mal ein anderer, primitiverer Ansatz: Entscheidend ist doch, was die CPU
sieht. Also brauchen wir den Adressbus und den Datenbus direkt an der
CPU. Der Abtastzeitpunkt wird durch /MEMRQ und /RD und /CE-Eprom
bestimmt, Feiglinge warten noch 20ns, bevor sie die Daten lesen.
Ja, so ist es. Das verwendete System macht es aber einfacher. Durch die Logic des Rombankings kann ich eben statt der /MEMRQ und /RD signale einfach das /OE neben dem /CE abfragen, weil genau das die Logic ist wie das ROM mal was ausgibt. Zwischen /MEMRQ und /RD hängt noch das GateArray des Schneider CPC, das daraus das /ROMEN signal erstellt, wenn ein ROM selectiert ist (also noch eine Bedingung die im GateArray Registern programmiert wird). Diese /ROMEN signal ist genau was am ROM direct als /OE anliegt. Daher ist es hier einfacher. Und ja, der Rest passt.
Zum Thema CPM BIOS, hast Du da eine gute kommentierte Disassembler Routine ? BTW. Ohne correcte Opcodes ist es dann weiterhin nicht zu lesen und es bedeutet leider auch nicht, das der Schneider CPC das dann genau so macht. Das fängt schon am IO handling an, und hört sicher bei der RST und CALL implementierung auf. Bin ich oben schon mal drauf eingegangen. Aber es könnte trotzdem helfen.
Ich habe ein Kommentiertes AMSDOS ROM, das ist das Original Amstrad was auch eine CPM Implementierung enthält. Weiterhin ist nicht nur das BIOS drin, sondern auch das BDOS, also weite Teile des Betriebssystems sind ebenfalls im ROM.
Harald K.
Reicht nicht, aus genau dem Grund, den "observer" angesprochen hat. Es
ist nicht ausgeschlossen, daß ein und dieselbe Adresse mal als Code
und mal als Daten angesprochen wird, d.h. mit aktivem und mit inaktivem
M1
Da habe ich mir Lange den Kopf drüber zerbrochen. Ich habe nach viel Überlegung am Ende entschlossen, daß ein und dasselbe Byte nicht Code oder Daten sein kann, weil es ansonsten via M1 zu vollkommen verschiedenen Ergebnissen führen würde und das nicht geht. Es kann ja kein self modifying code sein (weil es eben im ROM ist) und die Daten kommen vollkommen verschieden heraus je nachdem ob M1 oder nicht M1. Es gibt nur all 256 Bytes einen Bereich von genau 8 Bytes der immer unverschlüsselt ist, egal was für Bytes. Aber den genau zu finden und dann sowas da abzulegen wäre schon wirklich schwierig bei der Codeerstellung. -halte ich im Hinterkopf, beschäftige ich mich aber nicht mit.
Mi N.
Abgesehen vom "Wiehern" frage ich mich, wie Du auf 2 mA kommmst. Bei 3,3
V des RP2040, (maximal) 5 V der Z80 Logik und 0,5 V Durchlassspannung
der Eingangsdioden müssen ca. 1,2 V (5 - 3,8) Spannungsabfall
berücksichtigt werden. Bei 2,2 kOhm fließen da rund 0,5 mA.
Ich habe die vollen 5V angenommen ... aber das stimmen vermutlich Deine Überlegungen. Klar ist, je kleiner der Widerstand desto besser das Flankenverhalten.
Ob S.
Aber halt, neue Idee, viel einfacher: Man nimmt einfach zwei echte
ROMs. Eines enthält die "as is"-Variante, das andere die vollständig
verschlüsselte. Dann braucht man nur noch mittels M1 zu steuern, an
welches der beiden ROMs das OE für den aktuellen Cycle geht.
Ja, das würde sicher gehen, wenn man Hardware nachbauen möchte, und das geht sogar mit einem EPROM mit 32k total einfach. Einfach das M1 signal an A15 anlegen und fertig. In die unteren 16kbyte kommt dann das unveränderte ROM und in die oberen das encryptete ROM. Fertig ist das Ding komplett ohne weitere Logic. Interessanter Ansatz.
Aber es ist nicht wirklich das Ziel, was ich dahinter verfolge. Mein Ziel ist es den Code zu extrahieren und zu verstehen.
nicht nur die, auch die Adressleitungen, der Tausch ist ja statisch und
gilt immer und ist am ausgelöteten ROM oder EPROM nicht zu erkennen.
(Ich erinnere mich ganz dunkel kann die Quelle aber nicht mehr benennen)
Das ist aber nur ein sehr begrenzt sinnvoller kopierschutz, weil man dann mit der CPU immer noch einfach das komplette ROM auslesen kann.
Das ist hier genau verhindert worden, da einfach nur reads auf das ROM immer decrypted, was aber für Opcodes nicht passieren düfte. Also ist der ausgelesene Code auch falsch.
Also ich muß echt meinen Hut ziehen vor der Idee, die die Kollegen von Vortex damals hatten. Das ist echt unglaublich schwierig das zu Disassemblieren, weil immer der Verdacht besteht, das irgendwas nicht korrekt dekodiert wurde.
Kann man schon daran sehen, wieviel Brainpower hier jetzt schon reingesteckt wurde von Euch allen.
Wenn ich so einen Kopierschutz bauen würde, würde ich
natürlich auch dafür sorgen, dass es keine eindeutige Abbildungsfunktion
geben kann. D.h.: ein und dasselbe Byte im ROM mal als Opcode und mal
als Datum verwendet wird.
Das ist sicher eine sehr gute Idee, funktioniert aber mit diesem
verwendeten in Hardware implementierten Decoder nicht.
Natürlich würde das hervorragend funktionieren.
Der verwendete
Algorithmus ist auch so schon unmöglich zu hacken
Tsss... Ich habe damals(tm) noch ganz andere Sachen gecrackt. Diese ist vergleichsweise trivial.
Das war sicher damals
eher unmöglich
Nein. Es war damals zwar nur sehr schwer möglich, das zu cracken. Aber es war relativ einfach, das als Kopierschutz zu verwenden. Und genau deshalb wurde das eben auch gemacht. Möglichst einfach für den Ersteller, möglichst kompliziert für potentielle Cracker. Genau das macht einen "guten" Kopierschutz aus, jedenfalls aus der Sicht dessen, der die Entscheidung fällt, ihn einzusetzen. Damals wie heute übrigens, da hat sich nicht wirklich was geändert...
Das ist aber nur ein sehr begrenzt sinnvoller kopierschutz
kann sein, vielleicht war es auch nur einfacher für den Router!
(aber in der Zeit der gesteckten EPROM OS/Basic durchaus als Kopierschutz brauchbar, vor allem bei verschiedenen PCB Versionen, bei apple & cbm mir nie begegnet, da waren alle bytes schön in der Reihe sortiert.)
Wenn ich so einen Kopierschutz bauen würde, würde ich
natürlich auch dafür sorgen, dass es keine eindeutige Abbildungsfunktion
geben kann. D.h.: ein und dasselbe Byte im ROM mal als Opcode und mal
als Datum verwendet wird.
Das ist sicher eine sehr gute Idee, funktioniert aber mit diesem
verwendeten in Hardware implementierten Decoder nicht.
Natürlich würde das hervorragend funktionieren.
Praktisch nicht.
Du müsstest vorhersagen:
entweder welcher Opcode zu welchem Byte wird und das das zufällig genau das ist, was Du brauchst als Datenbyte.
oder genau den Bereich treffen wo M1 und /M1 das gleiche decrypting ergeben.
Das geht bestimmt irgendwie - aber realistisch nicht umzusetzen. Man muß sich dabei ja auch vor Augen halten, daß Vortex den Code damals warten können musste. Es gabe mehrer Versionen des ROMs. Je mehr ich von solchen "Tricks" einbaue, desto schlimmer wird das.
Und ich glaube, daß der Code auch so gut genug war. Es hat meines Wissens nach nie einen Nachbau gegeben.
Also scheint es einfach nicht wirtschaftlich gewesen zu sein.
Ich meine, daß was ich heute damit machen will ist ja auch was ganz anderes. Wenn ich es nur hätte kopieren wollen, dann hätte ich ja nur die Logik herausbekommen müssen (habe ich ja auch reverse engineered) und dann einfach nachbauen mit dem original ROM. Und wenn ich dann wie ich etwas weiter oben mal drüber nachgedacht hätte, dann hätte ein 32kb ROM das ganze komplett ohne die decoder logik gemacht.
Das reverse engineering trotz abgeschliffener Chips hat mich 2 Tage inklusive Schematic und vollkommener Logikanalyse über den Decoder gebraucht. Zugegeben ich konnte einen logic analyser in meinem Eprommer benutzen, der hat direkt rausgefunden, was die abgeschliffenen Chips waren. Und dann nur noch tracen, Schematic erstellen und voila - logik gehackt.
Ich bin da sicher kein experte und jemand in den 80er, der sowas designed hätte warscheinlich sehr einfach herausgefunden, welche 74er logicchip verwendet werden. Das ist ja auch kein Hexenwerk.
Insofern hat es nur dazu geführt, daß der Code nicht direkt gelesen werden konnte, hätte aber keine schamlosen Raubkopien inklusive der Logic verhindert. Und die CopyrightMessage zu verändern wäre jetzt schon gegangen :-)
Das ist aber nur ein sehr begrenzt sinnvoller kopierschutz
kann sein, vielleicht war es auch nur einfacher für den Router!
(aber in der Zeit der gesteckten EPROM OS/Basic durchaus als
Kopierschutz brauchbar, vor allem bei verschiedenen PCB Versionen, bei
apple & cbm mir nie begegnet, da waren alle bytes schön in der Reihe
sortiert.)
Ja, im CPC kommt das auch nicht vor. Alle originalen ROMs sind klartext.
Das ist eines der wenigen Module, wo ich das kenne. Ob das dann final den Aufwand überhaupt wert war... keine Ahnung.
Die haben auch eine RAM Erweiterung rausgebracht, das ROM darauf ist auch nicht encoded.
Vielen Dank für die vielen guten Anregungen und die spannenden Diskussionen.
Ich habe jetzt folgenden Pfad vor:
PICO nehmen.
Habe mir ein paar Levelconverter besorgt - werde aber erstmal mit Widerständen vor den Eingängen starten.
PIO verwenden um die Daten zu lesen
Dann drei Arrays verwenden.
Array für M1 und read tracking (ich habe ja genug Speicher - nochmal ein byte jeweils, FF wenn nicht gelesen 0 oder 1 entsprechend M1).
Array für Datenbyte.
Wenn das mit einer sehr genau bekannten und dokumentierten Funktion läuft, dann das Ding vollständig durchtesten insbesondere die Funktionen, die bei meinem Disassemblierten und Recompilierten Code im Emulator auf die Fresse fliegen.
Danke schon mal.
Wenn es Euch interessiert halte ich Euch gerne mit Details auf dem laufenden.
Du müsstest vorhersagen:
entweder welcher Opcode zu welchem Byte wird und das das zufällig genau
das ist, was Du brauchst als Datenbyte.
Für ein Asm-Programm ist das überhaupt kein Problem. Damals wurden tatsächlich auch noch viele Programme rein in Asm verfasst. Aber auch später war es kein Problem, da wurden dann halt vom SDK des Kopierschutz-Lieferanten entsprechende Asm-Einschübe im Code plaziert.
oder genau den Bereich treffen wo M1 und /M1 das gleiche decrypting
ergeben.
Brauchen sie doch auch garnicht. Du hast offensichtlich das Konzept nicht wirklich verstanden.
Das geht bestimmt irgendwie - aber realistisch nicht umzusetzen.
Du bist halt nicht aus der alten Garde, die noch wirklich bis in's Detail verstanden hat, was sie da tut...
Du müsstest vorhersagen:
entweder welcher Opcode zu welchem Byte wird und das das zufällig genau
das ist, was Du brauchst als Datenbyte.
Doch, das ist gut möglich. Eine "Kopierschutzroutine" prüft, ob ein an einer bestimmten Adresse gelesenes Byte einen bestimmten Wert hat - und diese Adresse ist halt eine Adresse, die im sonstigen Programmablauf einen Opcode speichert (d.h. mit aktivem M1 gelesen wird).
Wenn die Routine den korrekten Opcode liest, weiß sie, daß sie auf der falschen Hardware läuft, wenn die Routine den /anderen/ (verschlüsselten) Wert liest, kann sie annehmen, daß sie auf der richtigen Hardware läuft.
Wenn ich mich so erinnere, war der Reassembler selten richtig erfolgreich. Hinzu kam noch, dass durch Stack-Manipulation manches recht undurchsichtig wurde. Hut ab, wenns klappt!
Doch, das ist gut möglich. Eine "Kopierschutzroutine" prüft, ob ein an
einer bestimmten Adresse gelesenes Byte einen bestimmten Wert hat - und
diese Adresse ist halt eine Adresse, die im sonstigen Programmablauf
einen Opcode speichert (d.h. mit aktivem M1 gelesen wird).
Ach jetzt verstehe ich was Ihr wollt. Ja sowas ist natürlich extrem leicht machbar. Damit haben wir hier es nicht zu tun.
Lu schrieb:
Wenn ich mich so erinnere, war der Reassembler selten richtig
erfolgreich. Hinzu kam noch, dass durch Stack-Manipulation manches recht
undurchsichtig wurde. Hut ab, wenns klappt!
Hier geht es zuerst mal um das reine Disassembling. Der ein neue Assembling ist letzten endes nur Proof, das es auch korrekt ist.
Wie ich schon schrieb ist 95% schon klar. Ich weiß sogar genau was ich tracen will. Große bereiche sind schon zu 100% dekodiert und auch schon analysiert und kommentiert.
Es geht um die letzten 5%, die einfach in der realen Hardware einfacher zu sehen sind.
Quasi ein Hardware debugger - wobei leider die Stati der physischen CPU nicht so richtig zu sehen sind.
Das wäre natürlich der absolute hit.
Auf einem PICO den code mittracen, wissen wann ein Opcode komplett gelesen ist (inclusive Paramererbytes) (lookuptable für die Anzahl Bytes je Opcode etc) und dann einen Hardware Interrupt absetzen und den Z80 veranlassen die Register zu dumpen.
Ist allerdings grade bei diesem Thema hier schwierig, da wir es mit Hardwaresteuerung zu tun haben (Diskcontroller) und diese Firmware für viele Teile die Interrupts abschaltet, also ein NMI zu ganz anderen Problemen führen wird.
Im Schneider CPC gibt es Systemseitig keine NMIs, nur Maskeable-Interrupts.
Ist aber hier alles nur Schmuck am Nachthemd, weil es mir eher um die PICO Seite der Dinge geht und nicht so sehr zu argumentieren, was ein Kopierschutz alles könnte oder nicht könnte, sondern wie man diese spezifische Problem angeht.
Der einzige wirklich erfolgreiche Disasm ist das Hirn vor dem Bildschirm. Und das muss trainiert werden, wenn es gut sein soll. Speziell bei ASM Programmen muss man die Denke des ursprünglichen Programmierers erfassen, um effektiv zu arbeiten. Programme aus USA sehen anders aus als aus Fernost. Heute im Zeitalter der Hochsprache ist das anders. Da hilft es sehr, wenn man den verwendeten Compiler kennt und dann die Lib-Funktionen schon mal maschinell erkennen kann.
Der einzige wirklich erfolgreiche Disasm ist das Hirn vor dem
Bildschirm.
Ja, das sehe ich genauso.
Aber nach Meinung einiger Forumsteilnehmer stehen wir ja kurz davor, dass die KI das leisten wird...
Diese Sache wäre doch mal ein hübscher Test für diese KI-Gläubigen. Und sie hätten es ja noch richtig gut: sie könnten den Scheiß mit dem Wissen der bekannten "Verschlüsselung" und mit dem umfassenden Wissen über das Verhalten des Z80-Busses trainieren.
Aber ich sage: dieser nutzlose Dreck wäre nichtmal bei diesen hervorragenden Voraussetzungen in endlicher Zeit in der Lage, irgendwas brauchbares zu liefern...
nach langer Pause an dem Thema habe ich jetzt die letzten Tage versucht das Pico PIO projekt so umzusetzen.
Ich kann lesen etc. Aber ich bekomme murks dabei raus.
Mein code ist wirkich ziemlich simpel:
PIO Code
.program meinprogramm
.wrap_target
WAIT 0 GPIO 28 ;warten auf trigger input down
WAIT 1 GPIO 28 [9] ; warten auf trigger input high
nop [19] ; und insgesamt 250ns warten
in pins,22 (für den Test nur 14 bit adressbus und datenbus
.wrap
Das ganze wird mit autopush und eben automatischem Loop verwendet. Sind nur 4 PIO anweisungen. Soweit ich das verstanden habe, sollte das ohne Latenz funktionieren, also all 22 pins in einem Zyklus gelesen werden und dann danach direkt in den FIFO gestopft.
Das funktioniert aber nicht zuverlässig.
Im ARM code lese ich die FIFO, dann extrahiere ich die Adresse und das Datenbyte. Dann gucke ich ob in meinem Array das schon mal geschrieben wurde, und wenn ja vergleiche das Datenbyte mit dem im Array gespeichertem Wert. Ich habe zwei Flag - read und missmatch. read wird gesetzt wenn es schon mal gelesen wurde und missmatch wenn im Array was anderes steht. So kann ich also mit meiner dump() funktion schnell sehen, ob die Werte verändert sind.
Derzeit lese ich damit erstmal das normale OS ROM aus, welches ja vollkommen unverschlüsselt ist und auf jeden Fall immer die gleichen Werte ausgeben muß.
Das problem ist, daß ich mal die richtigen Werte bekommen (kann ich ja gut nachvollziehen, wie die sein sollen), und mal vollkommen anderes, quasi schon zufällig.
Ich hab ein bissl mit der Wait time rumgespielt um herauszubekommen, ob ich nicht richtig im Timing sitze, aber ich habe irgendwie das Gefühl, daß ich nicht alle bits gleichzeitig lese und zu einem Zeitpunkt Teilweise die bits schon den Wert verloren haben?. Irgendwie sieht ist das nicht stabil, wenn auch schnell genug.
Ich weiß, das ist aus der Ferne echt schwierig, aber ich würde mich über Anregungen freuen.
Ach ja, mein Trigger auf PIN28 ist derzeit ein sehr simpler NOR mit ~ROMEN und A14 drauf. Das heißt wenn A14 low ist, dann sind wir im OS ROM und ~ROMEN liegt direkt auf ~CE vom ROM. Wenn also beides LOW ist, dann sollte das richtige ROM angesprochen sein und dann bekomme ich meinen Trigger. ~ROMEN wird vom Gatearray produziert aus ~MREQ und ~RD, ist aber jedenfalls direkt der CE signal für den ROM chip.
Also sollte mir das Signal doch zuverlässig sagen, wenn das OS ROM angesprochen wird und es dürfte keine Abweichenden Einleseoperationen geben? Werde das jetzt noch mal mit einem direktem logicprobe auslesen, was der so sieht.
Spontane Frage, was ist mit A15?
Und wie sieht die Systemarchitektur in dem genannten Z80 system aus?
IMHO wäre es nach einem Jahr Stille im Thread und einer veränderten Aufgabenstellung sinnvoll, einen neuen thread mit angepassten Titel zu starten.
Das man mit einen RasPi-Pico aussreichend schnell sniffen kann, sollte jetzt klar sein. Und nicht zuletzt nach diversen Berichten über die Tauglichkeit einer pico-basierten Hardware für Arbeiten am ZX-Spectrum (ja, sind dort nicht 4 sondern 3,5 MHz):
" You can set the level shifters to use 5 V, 3.3 V, or an external voltage. Since [Happy] is working on a ZX Spectrum, the 5 V conversion is a necessity. "
Hier jetzt mal nachrichtlich, was ich nun im Ende gemacht habe:
Mit dem Pico habe ich einfach keine stabilen Signale hingebkommen, vermutlich schon deswegen, weil die verdrahtung mittels Perfboard an sich schon mist ist und die Kabel vermutlich die Gesamtsituation nicht so richtig verbessert haben.
Also anderes vorgehen.
3M ProbeKlammer 40pin DIP auf die CPU geschnallt und den Kingst LA5032 direkt an die CPU angehängt. Zusätzlich vom ROM dann die anderen Pins abgegriffen.
Dann das ganze mit sigrok gesampelt und durch den Z80 decoder gejagt.
Der Z80 decoder ist allerdings echte Grütze. Das Signal wird nicht am rising edge von /RD gesampelt, sondern am falling edge. Daher kommt der durcheinander weil die Datenleitungen flippen während des Signals.
Also Umprogrammiert und schon gibt es sehr stabile Ausgabe - direkt im Assembler.
Nun noch schnell mit ChatGPT ein python script geschrieben, daß mir addressen, datenbus bytes und instruktions (assembler) in Zeilen bringt und schon hat man was, mit dem man gut arbeiten kann.
Ich kann sogar sehen, was der BUS bei OUT,IN, PUSH etc macht und welche Werte konkret wohin geschrieben werden.
Also das macht dann das Disassemblieren noch einfacher. Jedenfalls für meinen Zweck jetzt super.
Ja, das gibt es und ja, es geht um das X-Modul. Aber erstens ist das die Version 1.0 (ich habe 2.11 (latest)) und zweitens ist das einfach nur der Dump, da sind wir gleich wieder am Anfang aller Probleme.
Ein Dump von 2.11 ist auch verfügbar (hab ich hochgeladen im CPC-Wiki).
Ich hab schon mal bei Vortex nachgefragt, doch leider haben die keinen Source code mehr. Die haben aber das ROM zur freien Verwendung offiziell freigegeben, können jedoch keinen Support leisten ;-)