Posts mit dem Label Homebrew werden angezeigt. Alle Posts anzeigen
Posts mit dem Label Homebrew werden angezeigt. Alle Posts anzeigen

Sonntag, 20. Mai 2012

CP/M 2.2

In der letzten Zeit habe ich mich wieder etwas mit CP/M beschäftigt. Vor einem Jahr ungefähr hatte ich auf meiner anderen Selbstbauplatine ja auch ein rudimentäres CP/M laufen. Nun habe ich die Arbeit wieder ausgegraben, das BIOS auf mein neues System angepasst und einen anständigen Bootloader programmiert. CP/M startet nun von der Compact Flash-Disk und kann nur-lesend auf ein 8 MB Dateisystem zugreifen.
CP/M 2.2 startet auf meinem Z80-System
Schreibzugriffe habe ich noch nicht implementiert. Das ist nicht ganz so einfach zu handeln, da CP/M 2.2 mit 128 Byte-Sektoren arbeitet, auf der Compact Flash-Disk die Sektoren aber 512 Byte groß sind.

CP/M besteht im wesentlichen aus drei Teilen: Das BIOS, BDOS und CCP. Um CP/M auf einen Rechner (mit kompatiblen Prozessor) zu portieren, muss man die BIOS-Routinen entsprechend anpassen. Das Betriebssystem benutzt dann die BIOS-Routinen und muss nicht wissen, wie die Hardware des Computers funktioniert. Das BIOS ist also eine Sammlung einfacher Treiber für Ein-/Ausgabe, Diskettenlaufwerkszugriff usw. Hier ist eine Übersicht der Funktionen.
BDOS ist das Betriebssystem an sich, welches u.A. das CP/M-Dateisystem versteht. Und CCP ist der Kommandozeileninterpreter, eine Art rudimentäres COMMAND.COM bzw. CMD.EXE.

Als erstes braucht man also eine BDOS- und CCP-Version, von der man auch weiß, an welche Stelle im Speicher es kopiert werden muss. Ich habe dieses hier verwendet. Es gibt dort sogar Assembler-Quellcode zu CP/M.
Als zweites nimmt man ein BIOS-Grundgerüst (die findet man zu Hauf im Netz) und passt die wichtigsten Routinen an das eigene System an. Funktionen für Lochkarten kann man glücklicherweise weglassen :-)
Neben den I/O-Funktionen für Bildschirm und Massenspeicher ist die BOOT-Funktion zu implementieren. Sie kopiert die Systemspuren von Diskette in den Speicher. In den Systemspuren stecken BDOS und CCP. Danach wird der CCP gestartet, der sich mit dem A0> Prompt meldet. Die CP/M 2.2 Meldung zuvor ist im BIOS implementiert und kann beliebig geändert werden.

Der Prompt A0> steht übrigens dafür, dass aktuell auf Laufwerk A mit Benutzer 0 gearbeitet wird. CP/M kennt keine Verzeichnisse, aber die Dateien auf einer Diskette können in Benutzerbereiche aufgeteilt werden.
Wie bei MS-DOS bzw. Windows kann mit der Eingabe von B: auf Laufwerk B gewechselt werden. Tatsächlich ist CP/M hier das Vorbild für MS-DOS und somit auch für Windows gewesen. Durch die Eingabe von user 1 kann auf Benutzer 1 gewechselt werden. Lässt man die Dateien auf Diskette auflisten (DIR), werden nur die Dateien des aktiven Users angezeigt.

Nun hat man also ein einfaches BIOS mit BOOT-Routine geschrieben. Jetzt stellt sich nur noch die Frage, wie das BIOS beim Einschalten in den Speicher gebracht wird. Dazu muss ein Bootloader-Programm geschrieben werden, welches zusammen mit dem BIOS üblicherweise im ROM liegt. Der Bootloader kopiert nun das BIOS an das Ende des RAM-Speicherbereichs. Bei mir ist das ab Adresse 0xFA00. Dann wird das ROM abgeschaltet, an die Stelle RAM gesetzt und der BIOS gestartet. Ab jetzt hat CP/M die Kontrolle über das System.

In den Screenshots stammt die Meldung "Loading CP/M..." vom Bootloader, der das BIOS in den RAM kopiert. Die Meldungen "Z80 Homebrew Computer" und "CP/M 2.2 Copyright (c) by Digital Research" stammen vom BIOS. Der Prompt A0> wird von CCP (mit Hilfe der BIOS-Funktion CONOUT) getätigt.

Da das System noch keine VGA-Schnittstelle oder sonstiges Display hat, findet Bildschirmein-/ausgabe über ein serielles Terminal statt. Im Folgenden ein paar Screenshots vom System in Aktion. Die CP/M-Software stammt von der Seite http://www.retroarchive.org/.

Microsoft BASIC 5.21 vom 28.07.1981 :-)

Noch ein Microsoft BASIC, von 1977!

Tiny-C Compiler

Turbo Pascal 1.0 von Borland
Ich habe ja damals Programmieren in Turbo Pascal unter MS-DOS gelernt, deswegen muss ich mir Turbo Pascal 1.0 mal genauer anschauen!

Ein CP/M-Dateisystem-Image kann man mit cpmtools erstellen. Es lassen sich damit Dateien rein- und rauskopieren. So wird ein einfacher Dateiaustausch zwischen CP/M und der modernen Computerwelt auf Dateiebene möglich. Um dieses Image auf die CF-Karte zu bringen, habe ich (unabhängig von CP/M) ein kleines Programm geschrieben, welches Daten im Intel-Hex-Format empfängt und als Binärdaten auf CF-Karte schreibt. Da aus 16 Byte Binärdaten im Hex-Format ungefähr 40 Byte werden, wurde aus dem erzeugten (nicht vollen) Dateisystem von 800 kB ca. 2,2 MB. Um das über die serielle Schnittstelle mit 19200 Baud zu übertragen, braucht es schon eine Weile. Da muss ich mir noch etwas Besseres überlegen.

Damit CP/M den Diskzugriff korrekt durchführt, muss im BIOS der Disk Parameter Header und Disk Parameter Block entsprechend der Disk-Geometrie konfiguriert werden. Auf dieser Seite steht, wie man die BIOS-Konfiguration bestimmt.
Da CP/M 2.2 nur mit 128-Byte Sektoren arbeitet, wie es damals bei 8"-Disketten üblich war, mein System aber eine CF-Karte über IDE anspricht, welche mit 512-Byte Sektoren arbeitet, muss hier eine Übersetzung stattfinden. Digital Research hat dafür ein Grundgerüst für das sogenanntes Blocking/Deblocking vorbereitet, das genau dieses "Sektormapping" durchführt. Diese Dokumentation findet man hier. Es funktioniert bei meinem System allerdings noch nicht.

Sonntag, 19. Februar 2012

Arbeiten am Ext-Board

Das Platine des Ext-Board ist endlich da und ich habe begonnen, es zu bestücken. Die IDE-Schnittstelle funktioniert bereits und der AY-3-8912 Soundchip auch. Es fehlt noch die Audio-Verstärkerschaltung für den Lautsprecher, da fehlen mir Bauteile.

Ich habe einen 64-Pin dsPIC30F6011 im Schaltplan, jedoch einen dsPIC30F6011A aufgelötet. Ich war so naiv anzunehmen, dass die Pinkompatibel sind. Es unterscheiden sich jedoch zwei Pins: Die Programmierpins PGC und PGD wurden von Pin 15/16 auf Pin 17/18 verschoben. Deswegen hat der PICkit 3 den µC anfangs nicht erkannt. Glücklicherweise habe ich Pin 17/18 auf einem Pinheader herausgeführt, so dass ich keine Änderungen an der Platine vornehmen muss, sondern nur am Programmierkabel.
Der dsPIC ist in einem 64-Pin TQFP-Gehäuse untergebracht. Es war das erste Mal, das ich so kleine Pinabstände gelötet habe. Mit reichlich Flussmittel und Entlötlitze war das Auflöten aber relativ problemlos.
dsPIC6011A im 64-Pin TQFP-Gehäuse
Noch ist nicht alles bestückt: Der Ethernetcontroller fehlt, die Ethernetbuchse und ein 16-Bit Portexpander sind noch nicht aufgelötet.
Das Z80-System

Donnerstag, 5. Januar 2012

AY-3-8912

Ich habe es nun endlich geschafft, dem AY-3-8912 ein paar Töne zu entlocken. Der Baustein ist offensichtlich sehr Timing-empfindlich, z.B. müssen innerhalb von 50ns die Bus-Steuerleitungen ge-setup't sein. Da ich die Busleitungen mit nem Mikrocontroller gesteuert habe, muss man schon überlegt verdrahten und programmieren  :-)

Der Chip wird mit einem externen Takt im Bereich 1-2 MHz getaktet. DA0-DA7 sind Datenbus und Adressbus, die sind gemultiplext. BC1 und BDIR sind die Bussteuerleitungen, die das kritische Timing erfordern. A8 und BC2 sind eine Art Chipselect und können fest auf +5V gelegt werden. CH_A-CH_C sind die drei Audiokanäle, die man einfach stumpf verbinden und in einen Kopfhörer einspeisen kann. IOA0-IOA7 ist ein digitaler "User-Port".

Der AY-3-8912 besitzt 16 interne Register. Um diese Register zu beschreiben/zu lesen, gibt es diesen prinzipiellen Ablauf:
Zum Schreiben versetzt man den Bus in den Inaktiven Zustand, dann wird die Adresse des zu selektierenden Registers auf den Bus DA0-DA7 gelegt und der Bus in den Latch-Zustand und zurück in den Inaktiven Zustand versetzt. Beim Übergang in den Inaktiven Zustand wird die Registeradresse gelatcht. Nun wird der in das Register zu schreibende Wert auf den Datenbus gelegt, in den Write-Zustand und zurück in den Inaktiven Zustand gewechselt. Nun wird der Wert in des Register geschrieben.

Den Buszustand legt man mit BDIR und BC1 fest. Der Inaktive Zustand wird durch BDIR=low und BC1=low gesetzt. Zuerst hatte ich BDIR an Portpin RB5 und BC1 an Portpin RE3 des dsPICs angebunden. Wenn man dann mit

_LATB5=0; 
_LATE3=0; 

den Bus steuern will, geht das schief, da jede Anweisung deutlich länger als 50ns benötigt, zumal der dsPIC auch nur mit 8MHz internen Takt ohne PLL läuft. Der Pegelwechsel selber von 0V auf 5V dauert ca. 7ns, das ist also kein Problem. Es müssen nur beide Pins gleichzeitig wechseln, um die 50ns nicht zu überschreiten. Also müssen beide Pins auf dem selben Port liegen. Um dann beide Pins gleichzeitig zu schalten, kann man dann das gesamte Portregister schreiben:

LATE = LATE & 0xFFFC;

Hier werden beim Port E die ersten beiden Bits gleichzeitig genullt. Möchte man ein Bit nullen, aber das andere "einsen", muss man einen Zwischenschritt einlegen:

LATE = (LATE & 0xFFFC) | 0x01;

So wird nur das zweite Bit genullt, das erste Bit gesetzt.

Den AY-3-8912 kann man auf diese Weise an den Z80 anbinden:
/AY_CS ist das Chipselect des Z80. Mit der untersten Adressleitung A0 des Z80 kann man so bestimmen, ob in das Adresslatch des AY-3-8912 geschrieben werden soll oder in eines der dadurch adressierten Register.

Versuchsaufbau

Dienstag, 5. Juli 2011

Ein paar Links

Um mal wieder etwas zu posten (IDE-Platine ist noch nicht fertig), hier ein paar Links:

Der Z80-Assembler, den ich nutze (Win32):
http://www.tni.nl/products/tniasm.html

Ein C-Compiler, den ich in Zukunft nutzen möchte:
http://sdcc.sourceforge.net/

Andere Z80 Projekte im Netz:
http://www.cosam.org/projects/z80/index.html
http://www.nathandumont.com/node/216
http://benryves.com/projects/z80computer
http://z80homebrew.blogspot.com/

Sonntag, 16. Januar 2011

Z80 Computer

Der Z80 ist ein 8-Bit Prozessor aus dem Jahre 1976. Verwendet wird/wurde er z.B. im Nintendo Gameboy, TI-Taschenrechnern und vielen Arcade-Game-Automaten. Er ist leistungsmäßig verlgeichbar mit dem 6502, der in C64 Computern zu finden ist.

Z80 im 40 Pin DIP-Gehäuse

Mein Ziel ist es, einen kleinen funktionsfähigen Computer um diese CPU herum zu bauen. Ich habe mir diesen Prozessor ausgesucht, weil der Chip noch gut für einen Hobbybastler zu "handlen" ist, und auch der Verdrahtungsaufwand - im Gegensatz zu moderneren Chips - relativ gering ist. Die Z80-CPU besitzt einen 8-Bit Datenbus und einen 16-Bit Adressbus. Es können also bis maximal 64kB RAM direkt adressiert werden. In meinem ersten Design soll er mit 1 MHz betrieben werden.

Und dies ist das Pinout des guten Stücks:

Die Ax-Leitungen sind die Adressleitungen, Dx sind die Datenleitungen. An die Phi-Leitung wird der CPU-Takt angelegt.

Für ein Minimalsystem muss ROM und RAM angeschlossen werden. Das ROM enthält den "Bootstrap"-Code, um den Rechner zu starten. In diesem Projekt wird es wohl gleich das ganze Betriebssystem sein :-)
In den RAM können dann weitere Programme aus externen Quellen nachgeladen werden und er wird als Arbeitsspeicher benutzt.

Wird die CPU neu gestartet, liest sie vom Byte an Adresse 0 (dem ersten Byte des Speichers, in diesem Projekt liegt es im EEPROM) an den Speicher aus. Das "Betriebssystem" muss also gleich am Anfang des Speichers liegen. Wie kommt es dort hin? Dafür werde ich einen Mikrocontroller vorsehen, der über eine serielle Schnittstelle mit einem PC verbunden ist. Auf dem PC wird man so die Software entwickeln und kompilieren können, um sie dann in den EEPROM zu schreiben. Der µC wird ebenfalls den RAM lesen und schreiben können. So werde ich dann auch anfangs die Funktion der CPU prüfen können, da sie ja noch keine Schnittstelle zur Aussenwelt hat.

Ich teile das Projekt für den Anfang erstmal auf zwei Karten auf: Die Prozessorkarte, auf der die CPU, der 1MHz-Quarz und Bustreiber sitzen. Auf der anderen wiederum Bustreiber, EEPROM, RAM und der Controller.

Folgende Bauteile habe ich ausgewählt:
  • Zilog Z80 CPU 4MHz
  • 1 MHz Quarz
  • Invertierender Schmitt-Trigger 74HC14
  • Bustreiber 74HC244, 74HC245
  • Controller dsPIC30F4012
  • 8kB EEPROM Atmel 28c64
  • 128kB RAM 628128
  • NAND 74LS00
  • OR 74LS32
...sowie natürlich die obligatorischen 100nF an jedem IC.

Das Ganze werde ich auf Lochrasterplatinen aufbauen und mit Kupferlackdraht verdrahten.