Home | Informative Texts | Apple 1

Apple 1 - Hardware und Software Analyse


Inhalt

Einführung

Hintergrund für die Entstehung dieses Textes waren Disukussionen zwischen mir (Elektro Ingenieur und Lowlevel/Embedded Programmierer sowie Retrocomputer Aktivist) und zwei Sportkollegen (beide Informatiker und Applikation Programmierer). Dabei entstanden auch Diskussionen zu moderner Bloatware, sowie als Gegenargument dazu wie die Rechner früher mit weit weniger Programm auskamen. Ein Teil davon waren auch kompakte Betriebssysteme im niederen 2-stellig kByte (typisches Homecomputer Basic System) oder gar 1-stellig kByte (Maschinencode oder Assembler oder kleinere Forth Rechner) Bereich ein Thema.

Irgendwann habe ich den superkompakten Maschinencode Monitor vom Apple 1 entdeckt, der es schafft mit nur 256 Bytes an Betriebssystem zu laufen. Nein, das sind keine 256 kBytes, nur 1/4 kByte! Nachdem ich dieses gegenüber einem der Kollegen beschrieb konnte er es kaum glauben. Danach hat sich diese Situation identisch wiederholt mit dem zweiten Kollegen.

Ich habe dem ersten damals versprochen, die Funktion dieses Systemes zu erklären. Bei Notizen dazu zusammenstellen habe ich aber bald gemerkt, dass dies auch andere interessieren könnte, weshalb daraus dieser Text entstand. Ziel davon ist den Apple 1 anzuschauen, sowohl von aussen was der Benutzer sieht, aber vor allem auch von innen was ein Programmierer davon sieht, sowohl die Hardware wie auch die Software davon im Detail. Dabei ist die zweite Hälfte vom Text zugleich ein Fall von Softwarearchäologie, etwas man ziemlich selten zu sehen bekommt.

Historische Situation

Die Geschichte des Apple 1 ist weitherum bekannt. Ich werde sie daher hier nur kurz zusammenfassen, plus noch ein paar unbekanntere Sachen addieren. Dem Namen nach ist es offensichtlich der erste Rechner von Apple, aber das ist das am wenigsten interessante daran.

Schon interessanter ist, dass er nicht von Apple entwickelt wurde. Das ganz einfach, weil Apple zu der Zeit noch gar nicht existierte! Vielmehr war es ein Hobbyprojekt von Steven Wozniak gewesen, der damit seine Kollegen beeindrucken wollte, darunter auch Steve Jobs. Die Firma Apple wurde erst danach von den beiden gegründet, um den bereits fertigen Rechner in Serie herzustellen und an Interessierte zu vertreiben.

Davon wurden je nach Quelle etwa 200 oder 600 Geräten produziert, plus einige mehr später aus Restbeständen, bevor er durch den revidierten und erweiterten Apple II abgelöst wurde. Dementsprechend ist er selten, trotz im April 1976 eingeführt, und der Apple II erst im April 1977 ein Jahr darauf erschienen. Davon sollen heute nur noch etwa 50 existieren, welche mit den vielen Apple Fans in der Welt sehr gesucht sind und demensprechend sehr teuer, immerhin $5-stellig!

Weit interessanter ist daher, dass es als Folge davon diverse Formen von Nachbauten gibt. Manche davon sind Chip-für-Chip genaue Replikate, trotz dass manche der Bauteile fast nicht mehr erhältlich sind, bei Restpostenhändlern oder gar Elektronikausschlachtern erjagdt werden müssen. Für bessere Replikate werden sogar Chips aus dem genauen Fabrikationsjahr zusammengesucht, für extreme sogar nur in den Gehäuseformen und nur von den Herstellern von denen Apple nachweislich Exemplare verbaut hat. Und selbst diese sind trotz den Arbeitskosten und Bauteilkosten weit billiger als die seltenen Originale, "nur" $4-stellig. Mir bekannt ist hier die Mimeo 1 Platine, blank geliefert, zum selber bestücken. Eine solche ohne Anspruch auf exakte Bauteile bestückt kommt auf etwa $700 Materialkosten.

Viel häufiger als Nachbauten sind daher nur funktionale Clone, mit nur den relativ einfach zu beschaffenden Teilen vom eigentlichen Rechner genau gleich und somit softwarekompatibel, aber die weit selteneren Terminal Logik Bauteile und die ebenso recht seltene Tastatursorte durch neueres vergleichbar wirkendes ersetzt. Selbst im eigentlichen Rechner werden die alten inzwischen seltenen kleinen Speicherchips durch moderne grosse ersetzt. Mir bekannt sind hier die A-One und Replica 1. Solche kosten etwa $150 als Bausatz aus Platine und Bauteilen. (Was hier leider gar nicht hilft, ist dass manche Cloner ihre Projekte als Replikate bezeichnen, und andere Leute auch noch volle Replikate als Clone bezeichnen!) (Logischerweise gibt es auch Emulatoren, als reine Software auf PCs. Mir bekannt ist hier der POM-1.)

Als Folge der vielen Apple Fans und dem Preis davon, wird der Apple 1 heute massiv überschätzt. Dabei wird er oft als "ersten Homecomputer" ausgegeben. Was er aber nicht ist, weil er technisch gar kein Homecomputer ist, sondern ein Zwischenschritt von Mikrocomputer zu Homecomputer, wie weiter unten erklärt wird. Was genau ihn zu technisch interessant macht.

(Der Apple II ist dagegen ein echter Homecomputer, aber nicht mehr der erste. Parallel zum Apple II kamen noch Commodore PET 2001 in Juni 1977 und Tandy Radio Shack TRS-80 Model I in August 1977, beide auch echte Homecomputer, zusammen als "das Trio von 1977" bekannt. Davor kam aber schon der Processor Technology Sol-20 in Juni 1976, und wurde damit der erste echte Homecomputer. Dazu mit schnellerem Prozessor, und komfortablerer Software. Aber mit $2000 auch weit teurer, wenn auch dafür vollständig betriebsbereit. Während der Apple 1 zwar nur $666 kostete, aber auch nur als nackte Platine ohne Tastatur und ohne Transformator+Gehäuse daherkam. Wobei man den Sol-20 auch als nackte Platine haben konnte, oder gar als Bausatz aus Platine und Bauteilen mitsammt Anleitung zum selber löten. Ebenso konnte man den Apple 1 sogar als blanke Platine ohne Bauteile zum selber löten bekommen. Wobei diese vermutlich Restbestände waren.)

Technische Position

Das ist die historische Situation. Aber die technische Position macht den Apple 1 erst richtig interessant, weil er eben ein Zwischenschritt ist, der zudem nicht direkt weiter verfolgt wurde, und somit praktisch alleine da steht. Die 1970er/1980er Mikroprozessor basierten Rechner kann man ansonsten in drei Kategorien einteilen:

Mikrocomputer-artige mit Terminals

Diese sind von Minicomputern her inspirierte Rechner, aber auf Mikroprozessoren basiert. Angefangen als Kisten mit Lampen und Schalter Frontpannels. Wie bei den MITS Altair 8800 und Imsai 8080. Dieses direkt kopiert von PDP-8 oder PDP-11 sowie vergleichbaren Minicomputern. Darin als Boden ein passiver Bus und dahinter/daneben ein Netzteil. Darauf gesteckt eine Prozessorkarte, sowie Speicherkarten und IO Karten. Generell passen hier alle S-100 oder ECB Bus Rechner drin. Wieder deren Busse direkt kopiert von PDP-8 Omnibus bzw PDP-11 Unibus sowie vergleichbaren.

Daneben kamen noch kleine Rechner mit 7-Segment Hexcode. Wie beim KIM-1 oder Mikroprofessor 1. Trotz total anderst aussehend, sind sie auch inspiriert von PDP-8/A und PDP-11/34 sowie vergleichbaren. Wenn auch hier als einzelne Platine ausgeführt statt als Kiste, mit nur einem Erweiterungsstecker statt einem Bus. Ausgelegt auf Lernsysteme statt Produktionssysteme.

Dann wurden erstere Kisten erweitert mit seriellen RS232 Karten und daran Terminals. Diese wurden schon bei den Minicomputern als "gläserne Fernschreiber" eingeführt, brauchten daher Fernschreiber kompatibeles ASCII plus Terminal spezifische Steuerzeichen. Dazu kamen noch Floppydisks und passende Disk Betriebssysteme. Wie bei den meisten CP/M Rechnern. Dies angelehnt an praktisch alle Minicomputer, welche wegen Timesharing und mehreren Benutzern sowie deren Distanz zum zentralen Rechner Terminals brauchten, sowie auf Harddisks basierte Software hatten. Nur hier auf nur ein Benutzer und ein Terminal reduziert, sowie auf kleinere billigere Floppys laufend. Die Terminals selber waren anfangs reine TTL Logik, wie bei der VT05 (um 1970), dann mit einem TTL Prozessorchen, wie bei der VT52 (um 1975), aber bald mit einem eigenem Mikroprozessor, ab VT100 mit einer 8080 drin (um 1980) und vergleichbaren.

Dann wurde wegen nur ein Benutzer und ein Terminal die Terminal Logik direkt im Mikrocomputer eingebaut, mit extern nur einem Videomonitor. Die einzige bei Minicomputern seltene Bauweise. Diese Logik sass zumeist als Terminalkarte auf dem Bus vom Rechner, weiter seriell angehängt, oder aber wegen kurzer Distanz auch teilweise parallel für schnellen direkten (Rechner-)Prozessor zu (Terminal-)Prozessor Datenaustausch.

Später wurde, weil nur ein Terminal, der Rechner nicht mehr als separate Kiste gebaut, sondern gleich in den Monitor eingebaut. Auch das war an die PDT Serie von Minis-in-Terminals angelehnt. Diese teils noch mit einem Bus voller Karten nebem dem Bildschirm, wie es die VT100 hatte, teils aber einfach eine grosse Platine unten drin, mitsammt Terminal Logik dort drauf, wie in den ADM-3.

Als Massenspeicher waren anfangs teils Lochstreifen, zumeist aber Floppys, selten Harddisks. Die Systemsoftware wird von diesen geladen, im ROM ist bestenfalls ein Monitor, oder auch nur ein Bootloader. Später mit den zumeist 5 1/4" Floppys im Terminal eingebaut, oft neben dem Bildschirm stehend, während 8" fast immer externe Laufwerke hatten. Was letzteres wieder an die PDTs angelehnt war.

Homecomputer-artige mit Videospeicher

Diese sind die logische Folge davon, dass es in neueren Terminals drinnen zumeist Mikroprozessoren hatte, und der 8008 als erster 8bit Prozessor sogar als solcher Terminalprozessor entwickelt wurde. Womit obige Mikrocomputer-artige Rechner die Situation erzeugten, dass ein Rechner Mikroprozessor trotz geringer Distanz per ASCII plus Steuerzeichen mit einem weiteren Terminal Mikroprozessor kommunizierte, was unnötig aufwendig war. Daraus entstanden als Vereinfachung die Homecomputer-artigen Rechner. Ein Prozessor wird hier direkt für Rechner und Terminal Funktion geteilt, kein zweiter. Strikte ist dies eine Form von Mikrocomputer, da auf Mikroprozessoren basiert, aber wegen sehr anderer Erscheinungsform kam bald ein neuer Begriff auf, der den alten verdrängte.

Es hat deswegen einen direkt vom gemeinsamen Prozessor beschreibbaren Videospeicher. Entweder direkt im Hauptspeicher (wie bei Apple II, VC-20 und C64 und C16,C116,Plus/4, Atari 800, Acorn BBC, Sinclair ZX80/81, Amstrad/Schneider CPC), oder in einem separateren Videospeicher (wie bei Sol-20, PET/CBM, TRS-80, Texas Instruments 99/4, Sinclair ZX Spectrum, alle MSX Rechner). Diesen muss der Prozessor aber stets selber adressierten und byteweise beschreiben und auch auslesen. Wenn auch bei manchen mit separatem Videospeicher nur indirekt adressiert durch Adressregister in der GPU setzen (wie bei 99/4 und MSX), aber die anderen direkt.

Es hat somit keinen Datenaustausch mehr, weder seriellen noch parallelen. Ebenso sind damit auch keine ASCII plus Steuerzeichen mehr nötig, um vom Rechnerprozessor zum Terminalprozessor via RS232 zu kommunizieren. Allerdings wurde oft in der Systemsoftware auf dem gemeinsamen Prozessor ein ASCII plus Steuerzeichen benutzender Treiber eingebaut, um einfache gradlinige Textausgabe zu vereinfachen, sowie portieren vereinfachen von auf Minis aufgekommenen Programmiersprachen und damit geschriebener Software. Diese war aber nur noch für Basisfunktionen wie die Kommandozeile zuständig, und stets umgehbar um ausgefalleneres wie Graphiken oder Spiele selber direkt zu machen, was erst bessere Ausgabe erlaubte.

Als entscheidender Unterschied wird hier nicht vom Terminal gescrollt, nachdem in einer Zeile kein Platz mehr ist, oder ein Zeilenvorschub geschickt wird, oder bestenfalls noch eine Escapesequenz, wobei solches nur nach oben, oder mit Escapesequenz auch nach unten geht. Mit neue Zeichen nur zeilenweise unten ansetzen, oder mit Escapesequenz auch oben. Vielmehr kann hier in beliebige Richtungen auf/ab/links/rechts gescrollt werden, sogar teilweise auf Videozeilen bzw Pixelspalten genau statt nur Textzeilen und Zeichenspalten. Sowie neue Zeichen zeilenweise oder spaltenweise angesetzt werden, unten/oben/links/rechts. Was gerade Spiele brauchen. Dazu kommen spezifische Fonts, oder aber Bitmapgraphik, oder beides schaltbar, was alles bei Terminals selten war (und bei Bitmap sehr langsam), aber hier bald zum Standard wurde (und bei Bitmap ausreichend schnell).

Als Massenspeicher war anfangs Kassettenband und später erst Floppys. Weil diese langsam oder gar sehr langsam sind, wird die Systemsoftware in ROM geliefert. Anfangs nur mit Monitor wie im Sol-20. Aber sehr schnell mit Basic, ab Apple II und PET 2001 und TRS-80. Und später mit ROM Modulen erweiterbar, wie in Atari 800 oder Texas Instruments 99/4 oder fast alles danach, diese gerade auch für Spielemodule wie Konsolen.

Personalcomputer-artige als Kombination

Diese sind die Kreuzung von beiden obigen. Einerseits wie beim Homecomputer einen direkt vom gemeinsamen Prozessor beschreibbaren Videospeicher. Anfangs zumeist separater Speicher auf einer Graphikkarte, später auch direkt im Hauptspeicher, aber stets vom Prozessor direkt adressiert (wie in Sol-20 und PET/CBM und TRS-80), ohne GPU Register (wie in 99/4 und MSX). Wieder mit einem ASCII plus Steuerzeichen Treiber, bzw bei IBM dem Video BIOS, aber ebenfalls umgehbar. Anderseits wie beim Mikrocomputer mit Floppys oder gar Harddisk. Mit Systemsoftware von diesen geladen, zumeist in ROM nur ein Bootloader, oder etwas mehr wie bei IBM das Disk BIOS, selten aber mit einem vollen Monitor. Strikte die letzte Evolutionsstufe vom Mikrocomputer, der die Vorteile vom Homecomputer in sich aufnahm, und dabei ebenfalls beide alten Begriffe fallenlies.

Apple 1 als Zwischenschritt

Der Apple 1 schafft es dagegen, aus diesen Kategorien komplett herauszufallen, bzw eine genau umgekehrte Kombination als bei den Personalcomputer-artigen zu sein.

Einerseits indem er analog zu allen Mikrocomputern für Video ein Terminal hat. Was keinen ASCII plus Steuerzeichen benutzenden Treiber braucht, und so erst die sehr kleine Systemsoftware erlaubt. Dies aber wie bei späteren Mikrocomputern mit Terminal Logik auf der einen Platine eingebaut, ohne Bus und Karten, und dazu noch parallel angehängt. Sowie in der Form mit externem Videomonitor, nicht Rechner in einem Terminal eingebaut. Aber trotzdem keine Kiste, nur eine Platine wie bei Hexcode Rechner. Und auch noch ohne eigenen Prozessor im Terminal wie bei frühen Terminals wie dem VT05.

Anderseits aber wie ein Homecomputer nur Kassettenband benutzt. Mit daher Systemsoftware im ROM. Allerdings das wiederum nur ein Monitor, Basic musste von Band geladen werden.

Damit ist er ein Zwischenschritt. Er wirkt von aussen wie ein sehr primitiver Homecomputer, und wird daher oft mit solchen verwechselt. Er ist aber intern fundamental immer noch ein Mikrocomputer, wenn auch dort ein eher eigenartiger, mit sehr alt und sehr neu bunt gemischt. (Der Apple II ist dagegen ein echter Homecomputer, aber der kommt erst nach dem Sol-20, ist somit nicht mehr der erste.)

Hardware Aufbau

Diese ist sehr minimal. Alles passt auf nur eine einzelne Platine. Siehe Photos der Bauteileseite und der Lötseite. Des weiterem ist die Hardware im Apple 1 Handbuch voll beschrieben als Schaltplan, auf 2 Seiten ohne Nummern, ganz am Schluss davon.

Auf dem ersten Platinenphoto sind an der Seitenkante Buchstaben A bis D und an der Längskante Zahlen 1 bis 18 zu sehen, welche als Koordinatensystem einzelne Chips identifizieren. Diese finden sich wieder, im Schaltplan auf allen Chips, oberhalb von deren Typenangaben. Sie werden hier im Text in Klammern referenziert, als "(an XX)".

Lieferzustand war als nackte Platine wie im Bild. Diese hat genau den eigentlichen Rechner (an A1..18 plus B1..18), sowie die Terminal Logik (an C1..15 und D1..15), sowie halbes Netzteil (nur die Niederspannung Stromaufbereitung) (an C16..18 und D16..18).

Es kamen keine Transformatoren oder sonstige Netzteil Hochspannung Teile mit, um die Stromaufbereitung zu versorgen. Es braucht 8..10V AC 3A, um 5V zu erzeugen, sowie 25..28V AC 1A mit Mittelabgriff um 12V und -12V zu erzeugen. (Damit ist dies ein kleines 5V*3A+2*(12V*1A)=39W Netzteil!) Die Transformatoren musste man selber separat besorgen, und verdrahten. Im Handbuch ist der Verdrahtungsplan dazu aufgezeichnet, und sind für 2 Lieferanten die passenden Bestellnummern aufgeführt! Ebenso gab es kein Gehäuse, der Benutzer musste eines selber herstellen. (Es wurden scheints sogar manche Apple 1 rein als blanke Platinen verkauft, ohne Bauteile drauf, der Benutzer musste dann sogar diese auftreiben und auflöten. Diese waren nach manchen Angaben aber vor allem Restposten nachdem die Fabrikation eingestellt war.)

Weiterhin kam keine Tastatur mit, also auch selber besorgen und verdrahten. Diese muss fertige 7bit ASCII Codes liefern mit Bit7=1. Im Handbuch hat es ebenfalls den Schaltplan vom Anschluss. Eine Tastatur von Datanetics wurde scheints von Steven Jobs empfohlen, was aber nicht im Handbuch steht, im Gegensatz zu den Transformatoren oben. Ebenso gab es keine Disklaufwerke oder Controller dazu. Es gab lediglich einen fakultativen Kassettenband Controller, als Karte welche auf den Slotstecker passt, an der man einen normalen Audio Kassettenrekorder anschliessen konnte.

Rechner

Als Prozessor (im Platinenbild an A6..8, und im Schaltplan letzte Seite an A7) läuft ein MOS Technology 6502 (der im Bild ist vom Zweithersteller Synertek). Dieser wird weiter unten in einem eigenen Kapitel detailliert beschrieben, hier nur seine Charakteristiken. Es ist ein reiner 8bit Prozessor, mit 8bit Datenbus und Speicher, ohne jegliche 16bit Operationen, ausser dass er 2*8=16bit Adressen generiert, und daher einen 64kByte Adressraum hat, als 256 Pages (0..255) zu je 256 Bytes (0..255) organisiert. Es hat dort keine Stromaufbereitung, da der Prozessor direkt mit 5V läuft, und auch keinen Kühlkörper oder gar Lüfter, da der Prozessor trotz 5V nur 160mA zieht und somit 5*0.16=0.8W an Leistung verbraucht, weshalb die Anschlussleitungen bereits zur Kühlung ausreichen. (Nachbauten benutzen den immer noch erhältlichen 6502 NMOS Chip, trotz von 1975(!). Clone zumeist die von mehreren Herstellern immer noch produzierte 65C02 CMOS Variante davon.)

Der auf 1MHz spezifizierte Prozessorchip ist mit 1.0227MHz um 2.27% übertaktet. Das weil dies 1/14 des verwendeten sehr billigen NTSC Farbfernseher 14.31818MHz Quarzes ist. Das Taktsignal "CL" kommt von der Terminal Logik her, nachdem es dort drin durch 14 geteilt wurde. Der Rechner wird also von der On-Board "Graphikkarte" her getacktet! Erstaunlicherweise hat es keinerlei Power-On Reset Logik, nur die Reset Taste auf der Tastatur. Da die 6502 keine interne Reset Logik hat, und die Platine keine solche addiert, stürzt sie nach einschalten immer ab, wird erst durch drücken der Reset Taste durch den Benutzer gestartet! (Selbiges gilt auch für die Terminallogik, also muss man aufstarten mit Strom einschalten, dann Bildlösch, dann Prozessor Reset. Designer Wozniak sieht dies nicht als Mangel, sondern als Feature, weil es hat ja schon die Reset Taste, wozu mehr? (Beim Apple II ist dies identisch, erst beim II Plus wurde eine "Autostart" genannte Reset Logik addiert.)

(Strikte kann die Platine auch mit einer Motorola 6800 laufen, aber diese Variante wurde nie verkauft, und war wohl nur als Notlösung gedacht, falls die neu erschienene 6502 scheitert. Das Handbuch weist im Schaltplan darauf noch darauf hin, aber auch dass man dazu all die fehlenden Bauteile innerhalb das gestrichelt umrandeten Teiles vom Schaltplan (unten links) auf der Platine (oberhalb von D2..5) nachrüsten muss, sowie 2 Lötpunkte (mit "6502" markiert) auftrennen muss.)

An Speicher gibt es 2 Bänke, wegen 8bit Prozessor aus je 8 Stück Mostek 4096 DRAM Chips (an B11..18 Bank X, bzw an A11..18 Bank W), welche pro Chip je 4k Adressen zu 1bit Breite haben, was pro 8-er Bank 4kBytes ergibt, was Betrieb mit 1 oder 2 Bänken erlaubt, und somit 4k oder 8k Speicher. Die 2*(4von6)=8 Stück 8T97 (an A9 und A10) hinter den DO Ausgängen der DRAMs sind reine Signalverstärker. Die 7410 (an B2) schaltet diese ein, falls Takt plus DRAMs selektioniert plus Lesezugriff.

Fast alle DRAMs brauchen um Pins zu sparen einen Adressmultiplexer, welcher nacheinander die Adressleitungen A0..5 bzw A6..11 als 2*6=12bit anliefert, bestehend aus 2 mal 74S257 (an B5 und B6). Dies braucht 3 Steuersignale, die DRAMs selber "RAS" für A0..5 gültig und "CAS" für A6..11 gültig, sowie die 74S257 "S" um umzuschalten. Diese werden aber nicht vom Prozessor geliefert, müssen also extern aus den Taktsignalen erzeugt werden. Das "S" wird von einem weiteren Signal "CLA" aus der Terminal Logik abgeleitet, welches nach Prozessortakt 1 zu 0 gehen auch kurz 0 ist, und dann durch 1 werden mit einer 74123 (an B3) einen Puls von 480ns erzeugt, welches falls eine 7410 (an B2) einen DRAM Zugriff erkennt als 0 an das "S" der 74S257 geliefert wird. Selbige 7410 ohne Einfluss der 74123, aber mit dem Prozessortakt "CL" in einer 7400 (an B1), erzeugt auch "RAS". Das "CAS" entsteht aus "RAS" durch die "Reste" der beiden 74S257 (je 4bit, nur 2*3bit werden für Adresse genutzt) mitschicken, so dass dieses nach umschalten der Adressen mit "S" von der ersten 74S257 (an B5) auch durchgelassen wird statt davor einem festen 1=inaktiv, aber durch die zweite 74S257 (an B6) noch etwas verzögert wird, um den geschalteten Adressen Zeit zu geben. (Diese 74S257 sind nebenbei auch noch die Signalverstärker für A6..11.)

Die DRAMs haben zwar 4 mal Kapazität von SRAMs, aber brauchen wegen "vergesslich" sein Refresh, sonst verlieren sie Bits ab 2ms nach beschrieben werden. Genauer sind die Bits in kleinen Kondensatoren in den Chips gespeichert, welche durch Leckstrom wegen nicht unendlich guter Isolation entladen werden, wobei der Hersteller nur garantiert, dass sie nicht vor 2ms zu weit entladen. Also müssen sie regelmässig ausgelesen und neu beschrieben/geladen werden. Die Adressen und Takte dazu werden ebenfalls von der Terminal Logik her genommen, welche anstelle von A0..5 eingespiesen werden, via weiteren 2 mal 74S257 (an B7 und B8). Diese werden von der Kombination von zwei Signalen aus der Terminal Logik getaktet, Signal "H6" wird während dem Zeichen anzeigen aktiv in Zeichen 00..39 und Signal "H10" (bzw in der Terminal Logik heisst es "10H") wird jeweils bei Zeichen 09,19,29,39 einer Zeile aktiv (aber zudem auch 3 mal wenn keine Zeichen am anzeigen, daher wird es hier mit H6 kombiniert). Der "Rest" der einen 74S257 (an B7) hält nebenbei auch noch den Prozessortakt während der Refreshzeit auf 0=inaktiv an, um so einen Speicherzyklus Wartezeit "auszuleihen", und der "Rest" der anderen 74S257 (an B8) schaltet zudem R/W auf festes 1=read, um den Speicher in diesem Zyklus nicht zu verfälschen. All diese "Restenverwertung" spart Bauteile, und ist dabei trotzdem sehr elegant. Wegen Refresh gehen zudem 4/65 aller Speicherzyklen verloren, was den Prozessor von den realen 1.0227MHz auf 61/65 bremst auf effektive 0.960MHz, wie auch vorne im Handbuch angegeben. (Die 2*2 74S257 sind nebenbei auch noch die Signalverstärker für A0..5. Womit nur noch A12..15 weitere 1*(2von6)=4 Stück von den 8T97 (an A9 und A10) brauchen.)

(Clone nehmen hier üblicherweise einen einzelnen 62256 SRAM Chip von 32kBytes. Das sind 8 mal die 4k von einer 8-er Bank hier, also 64 mal soviele Bits im Chip. Und das trotz dass SRAM 4 mal so viel Chip Platz braucht wie gleich viel Bits DRAM, diese also mit 256 mal DRAM vergleichbar sind! Dafür laufen SRAM ohne Adressmultiplexer und Refreshadressen, und somit auch ohne die 4/65 Bremse. (Diese *256 sind aber auch nur +4 Chipgenerationen zu je *4 grösser, mit Generationen in je 3 Jahren erscheinend, also 1977 +4*3 Jahren, auf Stand von 1989. Bereits 3 Jahre davor wären 6264 SRAM Chips von 8kByte benutzbar gewesen.))

Weiter gibt es eine Bank aus 2 Stück MMI 6301-1 PROM Chips (an A1 und A2), welche pro Chip je 256 Adressen und 4bit Breite haben, was zusammen ganze 256 Bytes an Platz bietet. Auf dem fakultativen Kassettenband Controller, hatte es weitere 2 Stück MMI 6301-1 PROM Chips, welche in ihren 256 Bytes dessen eigenen Treiber plus noch Kassetten Utilities mitbrachten, mit denen man auch Benutzersoftware abspeichern und wieder laden konnte. Zu diesem lieferte Apple einen 4k Basic Interpreter auf Kassette.

Ohne diese Erweiterung beinhalten die PROMs die ganzen vorhandenen 256 Bytes an Systemsoftware, im Handbuch einfach als "6502 Hex Monitor" betitelt. Dieser wird weiter unten detailiert beschrieben, sein Kommandozeilen Interface sowie seinen Programmcode, mit letzterem alleine etwa die Hälfte von diesem Text, weil er mehr als alles andere den Rechner charakterisiert, neben noch dessen Terminal Logik. Alle andere Software muss mit den Daten ins DRAM. (Clone nehmen hier einen einzelnen 2864 oder 28256 EEPROM Chip von 8kBytes bzw 32kBytes, ohne Kassettenband Software aber mit dem Basic Interpreter zumeist bereits geladen.)

An Ein-/Ausgabe hat es eingebaut nur einen einzelnen PIA Parallelport Chip (im Bild an A3..5, im Schaltplan an A4), ein Motorola 6820 oder 6821 (oder wie im Bild auch ein MOS Technology 6520). Dieser bietet 2 GPIO Ports A und B an, zu je 8 Datenleitungen PA0..7 und PB0..7 plus je 2 Steuerleitungen CA0..1 und CB0..1, also 20 Pins GPIO. Die Steuerleitungen sind vor allem für Handshake Signale gedacht, um gültige Daten anzuzeigen und abzunehmen. Daten vom einem sendenden Prozessor in PIA Port B Ausgabedaten Register geschrieben zeigt diese an Pins PB0..7. Dies aktiviert auch Pin CB2, welches mit einem empfangenden Prozessor seinem Pin CA1 verbunden wird. Dessen Zustand kann dieser in seinem Port A Status Register überprüfen. Gefolgt von die Daten von Pins PA0..7 via PIA Port A Eingabedaten Register lesen. Was wiederum Pin CA2 aktiviert, welches mit dem sendenden Prozessor seinem Pin CB1 verbunden ist, welches dann CB2 deaktiviert. Was dessen Prozessor in seinem Port B Status Register überprüfen kann. Gefolgt von danach weitere Daten zu senden, während der empfangende das deaktivierte CB2 via CA1 sehen kann, bzw genauer das erneut aktivierte CB2.

Die beiden Ports sind fest mit Funktionen belegt. Einerseits wird an Port A eine externe ASCII erzeugende Tastatur erwartet, welche an einem 16pin IC Sockel (an B4) als Steckbuchse angeschlossen werden muss. Dabei gehen Signale "B1..8" (Eingabedaten) an Pins PA0..7 (die Monitor Software in den PROMs braucht "B7" auf 5V), sowie "STR" (Taste bereit) an Pin CA1, mit Pin CA2 unbenutzt bleibend (mangels Rückmeldung). Anderseits ist an Port B die eingebaute Terminal Logik, mit Signale "RD1..7" (Ausgabedaten) von Pins PB0..6, sowie "DA" ("Zeichen wartet") von Pin CB2 und "RDA" ("Zeichen verarbeitet") an Pin CB1. Wobei RDA davor noch mit einer 74123 (an B3) um 3.5µs verzögert wird. Und CB2 welches auf CB1 reagiert hat, kann auch via PB7 gelesen werden. (Fakultativ kann man mit 2 Lötpunkten die IRQA und IRQB Pins der 6802 an die IRQ bzw NMI Pins der 6502 legen, aber die Monitor Software in den PROMs kann damit nicht umgehen.) (Clone benutzen auch hier den selbigen immer noch unverändert produzierten Chip von 1974(!), in einer der 6820 oder 6821 oder 6520 Varianten, zumeist den 6821.)

Weiter gibt es einen einzelnen Slotstecker (an A+B nach 18). Zudem gleich daneben (bzw im Bild darunter) einen Erweiterungsanschluss, an dem eine kleine Platine mit weiteren Slotsteckern angehängt werden kann. Letztere wurde selten genutzt, weil Apple nur eine einzelne Slotkarte anbot, das Kassettenband Interface, womit der eingebaute Slotstecker ausreichte. Dieses Interface war nicht fest im Rechner eingebaut, sondern eine eigene Platine falls man sie haben will. Siehe Photos der Bauteileseite und Lötseite.

Abgesehen von diesem hat der Rechnerteil der Platine noch einen Decoder 74154 (an B9..10), der den 64kByte Adressraum in 16 mal 4kByte Schnitte aufteilt, und damit 16 Lötpunkte ansteuert gleich daneben (im Bild rechts davon), welche mit "0".."9" und "A".."F" angeschrieben sind (von unten nach oben), passend zu den Hexadressen Blöcken 0xxx ... Fxxx.

Diese 4k Schnitte passen genau zu den 4k DRAM Bänken. Von denen ist die erste Bank X im Lieferzustand per Lötpunkt "X" mit "0" verbunden und somit in 0xxx. Die 6502 braucht 0000..00FF (Page 0, Zeropage genannt) und 0100..01FF (Page 1, Stackpage genannt) stets RAM, sonst versagen manche Funktionen. Die zweite W kann per Lötpunkt "W" wahlfrei an "1"xxx (1xxx, mit 0xxx für erweitertes 8k RAM, ist Lieferzustand) verbunden werden, oder an "E" (Exxx, für Basic, dorthin assembliert damit dieses auch in PROMs sein kann, was Apple aber nie angeboten hat) verdrahtet werden. Der Adressmultiplexer wird nur angesteuert, wenn eine der beiden "X" oder "W" von der 74154 aktiviert wird, aber auch stets direkt bei Refresh, alles von einer 7410 (an B2) gesammelt. Dies neben dass der Refresh die 74154 abschaltet, mit einer 7400 (an B1). Bei einem Apple 1 mit zweiter RAM Bank, aber ohne Drahtbrücke dort, wurde diese nie ausgenutzt, und es ist vermutlich ein "blankes" Replikat rein zu Ausstellungszwecken. (Im Bild ist eine zweite Speicherbank drin, und per langem Draht an Exxx gelegt, für Basic.) (Clone legen ihre 32k SRAM an 0000...7FFF, ohne 74154 Decoder.)

Die 256 Bytes PROM kommen ebenfalls im Lieferzustand per Lötpunkt "Y" mit "F" (Fxxx) verbunden. Darin erscheinen wegen 4k=4096Bytes Schnitte aber nur 256Bytes PROM diese 4096/256 = 16 mal wiederholt (F0xx = F1xx = F2xx = .. = FFxx). Die 6502 braucht in FFFC und FFFD ROM, sonst stürzt sie auch nach Reset drücken ab! (Clone legen ihre 8k oder 32k EEPROM an E000..FFFF bzw 8000..FFFF. Mit entweder Fxxx 16 Kopien vom Monitor und Exxx 1 Kopie Basic. Oder statt ganzes Fxxx nur FFxx 1 Kopie Monitor und F000..FEFF für anderes frei. Wie etwa den Krusader Assembler, der bei den A-One und Replica 1 Clonen vorinstalliert ist.)

Die 6820 kommt ebenso im Lieferzustand per Lötpunkt "Z" mit "D" (Dxxx) verdrahtet. Wieder wegen 4k=4096Bytes Schnitte aber die 6820 nur A0,A1,A4 ausnutzend 4096/32 = 128 mal wiederholt, darin die ersten 16 unbenutzt, und in den zweiten 16 dann 16/4 = 4 mal wiederholt. Das System braucht diese, stets als D010..D013 angesprochen, sonst kann es weder Ausgaben anzeigen noch Eingaben annehmen. Ein Apple 1 auf dem dort keine Drahtbrücke ist, kann als unbenutzbar und somit nie benutzt angeschaut werden, und ist sehr sicher ein Replikat, entweder gezielt im "Blankzustand" wegen "schöner" oder unwissend wegen "Draht = abgeändert" annehmen, statt im historischen Lieferzustand und Gebrauchszustand nachgebaut. (Im Bild ist dies per kurze Drahtbrücke der Fall.) (Clone legen diese an Dxxx bei 8k EEPROM, oder schneiden diesen Bereich bei 32k EEPROM aus diesem heraus.)

Weiter vorhanden sind Lötpunkte "R" "S" und "T", alles Slotstecker Signale, von denen das Kassettenband Interface "R" an "C" (Cxxx) braucht. Ein Apple 1 ohne Drahtbrücke dort wurde nie mit Kassetten benutzt. (Im Bild ist dies per mittlere Drahtbrücke auch der Fall.) (Clone lassen dies zumeist weg, weil Kassetten eh mühsam sind. Sie verlassen sich rein auf EEPROM. Bei 32k sind nach Fxxx und Exxx benutzt sowie Dxxx weg immerhin noch (8-3=5)*4=20k verbleibend.)

Terminal Logik

Neben 50% Rechner (an A1..18 und B1..18) und 10% Stromaufbereitung (an C16..18 und D16..18) hat die Platine noch 40% Terminal Logik um ein Bild zu generieren (an C1..15 und D1..15), welche dahinter nur noch einen Schwarzweiss NTSC Videomonitor braucht (im Bild Anschluss oberhalb von D1). Dieses ist reines Schwarzweiss ohne Graustufen. Trotz kleiner sein als der Rechner ist diese Logik weitaus schwieriger zu verstehen. Sie hat mehr Chips, ohne 2 grosse 40pin, mit diversere Sorten Chips weil ohne 2*8 identische DRAMs, und komplexere Logik.

Ein Videosignal ist 1V, aus 0.7V Differenz von dunkel nach hell plus 0.3V weglassen als "superdunkel" für Synchronisationspulse. Es kann am Stecker ganz oben rechts abgenommen werden. Es entsteht im Potentiometer R12 100Ohm (im Schaltplan zweitletzte Seite mitte links), getrieben vom Transistor Q5, mit dort Addition und Verstärkung der Stromstärken von den Chips 74166 (an D1) seinem Pin 13, plus 74175 (an C3) seinem Pin 6, durch die beiden Widerstände R1 1500Ohm bzw R2 3000Ohm, um mit für Pixel 1=hell=0.7V bzw 0=dunkel=0V plus Synchronisationspulse 1=ohne=0.3V bzw 0=mit=0V anzusteuern. Das ist der ganze Anteil an Analogelektronik, alles andere ist rein digital.

Eine Videozeile besteht aus 40 Zeichen, zu je 7 Pixel (davon 5 das eigentliche Zeichen und 2 Abstand dazwischen), also 40*(5+2=7) = 280 Pixel horizontal. Dazu wird der 14.31818MHz Quarz (an D13), mit einem Oszillator aus zwei viertel 7404 (an D12) betrieben, im ersten Viertel von einem 74175 (an C13) auf halbe Frequenz geteilt für die Pixelrate, von 7.159MHz, am Signal "DOT RATE". Zudem dient dies als Buffer für den Oszillator, der nur einen einzelnen Chip als Last haben darf, und es stellt sicher dass das 7.159MHz Signal symmetrisch ist, auch wenn das 14.31818MHz dies nicht ist. Dann werden 7 Takte davon abgezählt von einem 74161 (an D11), welcher wegen Q3 an CEP und CET verbunden bei Werten von 0..7 im nächsten Takt auf 10 lädt, somit ??,10,11,12,13,14,15,0,10,... erzeugt, unendlich. Das Bit Q3 hat dabei 1,1,1,1,1,1,0 mit =0 benutzt für das von 0 auf 10 zu springen.

Die 5+2=7 Pixel werden ausgegeben mit einem 74166 (an D1), der beim 15 zu 0 Wechsel ein neues Zeichen lädt, mit 5 Pixel vom 2513 Font ROM (an D2) Pins H+G+F+E+D erweitert mit 2 Leerpixel Pins C+B an 0V. Dabei gibt er gleich dessen erstes/linkes Pixel an Pin QH aus, aber schiebt bei allen anderen 6 Takten die weiteren geladenen Pixel von rechts nach, und ohne weiteres laden liefert er einfach Leerpixel (von Pins A+IN an 0V). Was zusammen die 14.31818/2/7=1.0227MHz Frequenz an Zeichen ergibt. Deren 40*7=280 Pixel passen so mit 39µs Signaldauer in etwa 4/5 der sichtbaren Videozeile von 51µs eines NTSC Monitors, was einen schwarzen Rand links und rechts ergibt.

Selbige 1.0227MHz bekommt auch der Prozessor als Takt gegeben, vom Bit Q2 der 74161 her, als Signal "CL", mit obige 10,11 und 0 also 0 sowie 12..15 als 1, für einem Wertverlauf von 0,0,1,1,1,1,0 0,0,1,1,1,1,0 ..., zu 4:3 asymmetrisch, um weitere Chips zu sparen. Zudem geht das Bit Q3 mit Signal "CLA" an den 74123 (an B3) für den 480ns Puls an den Adressmultiplexer auslösen.

(Was ich hier nicht verstehe ist, warum dieses Q3 neben an PE, auch an CEP und CET geht. Was bei Q3=0 den Zähler anhält, aber da gleichzeitig das PE aktiv ist und Vorrang hat, wird ein ansonsten erzeugtes 0+1=1 ohnehin sofort mit der 10 überschrieben, was dann mit Q3=1 gleich wieder den Zähler aktiviert. Am ehesten könnte es sein, dass die CEP und CET Pins nahe bei Q3 und PE sind, und nicht bei 5V=1. Aber man könnte sie auch einfach leer lassen, was default 1 ergibt, und bei der nachfolgenden 74160 auch gemacht wird, wobei das aber anfälliger ist auf Störungen.) (Im Apple II werden auch diverse Zähler und Schieber ihre unbenutzten Steuerleitungen an "SOFT 5" gehängt, einem stets 1 liefernden Ausgang einer 7400 mit Eingang = 0. Scheint den selbigen Grund zu haben, aber ebenso keinen ersichtlichen Vorteil gegen direkt an 5V für 1. Wohl eine Marotte vom Designer.)

Selbiges Bit Q3 geht aber auch als Signal "CHAR RATE" als Takt an eine Zählerkette von einer 74160 (an D6) und drei weiteren 74161 (an D7 bis D9) um diese 4*4bit zu takten. Diese erzeugen das ganze Videotiming! Die 74160 zählt zumeist 0..9=10 Zeichen, und schickt Signal "10H" an die erste 74161 (an D7). Selbiges geht als Signal "H10" zum Refreshmultiplexer. Die 74161 zählt 9..15=7 Zeichengruppen, und kürzt bei der ersten davon die 74160 auf 5..9=5, für insgesammt 1*5+6*10 = 65 Zeichen. Was bei 1.0227MHz dann 65*(1/1.0227)=63.557µs Zeilenzeit ergibt. (Was ich hier nicht verstehe ist, warum ein 0 zu 5 Anfangswert Schalter der 74160 und ein 0 zu 9 Anfangwert Schalter der 74161 vom Bit Q3 der 74161 her kommen, es nicht einfach feste 5 und 9 sind. Zumal der "LAST H" Ladeimpuls vom TC Pin nur bei 74160=9 und 74161=15 ausgelöst werden kann, womit letztere ihr Q3 stets 1 ist, und der Anfang so stets 5 bzw 9 wird. Ausser es ist auch obige "SOFT 5" Logik am Werk. Klar ist dagegen, dass die normale 0 als Anfangswert der 74160 nur intern entsteht, nach einem Überlauf ohne Ladeimpuls von der 74161.)

Dabei wird Bit Q2 dieser 74161 zu 0,0,0,1,1,1,1, was genau Zeichen ausgeben steuert, als Signal "/HBL", mit 1*5+2*10 = 25 als 0 = nicht ausgeben und 4*10 = 40 als 1 = doch ausgeben. Dieses steuert mit Q2=1 ob obige 74166 Zeichen laden darf, oder auf Leerpixel für den horizontalen Strahlrücklauf zurückfällt. Dabei wird zudem Bit Q0 zu 1,0,1,0,1,0,1, was zusammen mit Q2 bei beiden = 0 in einem 7432 (an C9) 1,0,1,1,1,1,1 den Horizontalsync Impuls erzeugt, Signal "/HSYNC". Insgesammt entstehen so in jeder Zeile 1*5 blank + 1*10 Puls + 1*10 blank = 25 "Zeichen" Rücklauf plus 4*10 = 40 ausgegebene Zeichen. Selbiges Q2 geht als Signal "H6" an den Refreshmultiplexer, um mit obigem Signal "H10" 4 mal pro Zeile aktiv zu werden, bei Zeichen 09,19,29,39, was die 4 von 65 ergibt. Zudem werden Q0 und Q1 als Signale "H4" und "H5" zu den Bits A0..1 der Refreshadresse.

Das Videobild besteht weiter aus 24 Textzeilen, zu je 8 Videozeilen (davon 7 das eigentliche Zeichen und 1 Abstand dazwischen), also 24*(7+1=8) = 192 Videozeilen vertikal. Diese Zeilen passen ebenfalls alle in etwa 4/5 der 240 sichtbaren Zeilen eines NTSC Monitors, was auch einen schwarzen Rand oben und unten ergibt, und zusammen mit Rand links und rechts den genutzten Textausschnitt vom Bildschirm. Das ergibt zusammen 40x24 Zeichen, mit 5x7 Font in 7x8 Feld, und somit insgesammt 280x192 Pixel an Bild.

Die obigen 63.557µs Zeilenzeit werden, mit dem "LAST H" Puls am Ende des letzten gezeichneten Zeichens, dem selbigem der auch die 74160 auf 5 setzt, an die verbleibenden beiden 74161 (an D8 und D9) weitergegeben. Diese zählen zusammen zumeist 0..255=256 Zeilen, mit 0..191=192 davon als Ausgabe plus 192..255=64 als vertikalen Strahlrücklauf, mit in letzteren dem Signal "VBL" aktiviert. Die Zeile 255 aktiviert zudem das Signal "LAST". Die 4 Bits der ersten 74161 gehen auch als Signale "V0..3" zu den Bits A2..5 der Refreshadresse. Damit dauert ein ganzer 64er Refreshdurchlauf (64/4=)16 Zeilen * 63.557µs, und somit etwa 1ms, was für die DRAMs welche nach 2ms anfangen zu "vergessen" problemlos ausreicht. Damit strikte doppelt schnell, aber Logik für nur 2 mal pro Zeile wäre aufwendiger!

Selbiger "LAST H" Puls geht parallel dazu, auch an eine weitere einzelne 74161 (an D15), welche bei nicht Strahlrücklauf oder in diesem bei Zeilennummern mit Bit V5=0 oder Bit V4=1 oder Bit V3=1 fest auf 10 gehalten wird. Sobald obiges nicht mehr gegeben ist, was nur bei Zeilen 224 bis 231 sein kann, zählt dieser los. Was 8 Schritte 11,12,13,14,15,0,1,2 gibt bevor er wieder auf 10 gehalten wird. Bei jeder Zahl mit Bit1=1 (11,13,15,1) werden mit dem Signal "VINH" die obigen beiden 74161 angehalten, was die Zeilenzahl von 256 auf 260 anhebt, näher zu den eigentlich vom NTSC Monitor verlangten 262. Bei jeder Zahl mit Bit3=0 (0,1,2) wird zudem der Vertikalsync Impuls erzeugt, und im 7400 (an C15) mit "/HSYNC" kombiniert und nach der 74175 (an C13) an den Analogteil gegeben.

(Bedingt dadurch wie Videomonitore funktionieren, sind es real 2*192=384 genutzte Zeilen, von bei NTSC bis zu 2*240=480 sichtbaren, aus insgesammt 2*262.5=525, von denen aber abwechselnd (interlaced) pro Bilddurchlauf nur die gerade bzw ungerade nummerierten gezeichnet werden (was offiziell 480i genannt wird). Von denen werden hier aber nur die geraden benutzt, mit dafür in jedem Bild diese gezeichnet werden (was offiziell 240p genannt wird). Ebenso sind es strikte 280 Pixelpaare und so 560 Pixel (von den bei 14.31818MHz in den 51µs insgesammt möglichen etwa 730), was zu "breiten" Pixeln führt, und zusammen mit nur den geraden Zeilen benutzen zu einem streifigen Bild.)

(Letzteres ist bei allen 1970er/1980er Videomonitor basierten Terminals und Homecomputern und Konsolen typisch. Es sollte bei gut gemachten Emulatoren auch so erscheinen, mit jede zweite Zeile schwarz gelassen. Zeilen statt dessen doppelt zeichnen (als "double scan" bekannt) sollte nur ein einstellbarer Spezialfall sein.)

Damit haben wir das Videotiming, nun zum eigentlichen Bildinhalt. Die 5x7 Pixel der Zeichen (im 7x8 Feld) kommen aus einem Signetics 2513 Font ROM mit 64 * 8x5Pixel Zeichensatz (an D2..3). Dieses kann nur für ganze 64 Zeichen je 8 Videozeilen a 5 Pixel liefern (wovon die obere Zeile immer 0 ist). Wobei diese Zeilen durch die Signale "V0..2" vom ersten der beiden 74161 ausgewählt werden. Das reicht nur gerade für teils ASCII 32..95 darstellen, also gibt es keine 96..127 und somit auch keine Kleinbuchstaben. Genauer werden ASCII 96..127 im Apple 1 einfach in 64..95 umgewandelt, durch ASCII Bit5 fallenlassen, womit diese zu Duplikaten der Grossbuchstaben werden. Bzw strikte werden beide zu den ROM Zeichen 0..31, nur die 32..63 sind an ihrem ASCIII Platz im ROM. Dies geschieht durch ASCII Bit5 fallen lassen und statt dessen Bit6 invertiert zum ROM Bit5 machen. Erst recht hat es keine Graphik, nicht einmal Blockgraphik, bestenfalls geht teilweise ASCII Art (es fehlen dafür aber manche Sonderzeichen welche mit den Kleinbuchstaben und Delete zusammen in 96..127 sind). Das resultiert in genau diese Zeichen nutzbar sein:

  ASCII   im Bildsp  im ROM    Zeichen

 32..47     0..15    32..47      ! " # $ % & ' ( ) * + , - . /
 48..63    16..31    48..63    0 1 2 3 4 5 6 7 8 9 : ; < = > ?

 64..79    32..47     0..15    @ A B C D E F G H I J K L M N O
 80..95    48..63    16..31    P Q R S T U V W X Y Z [ \ ] ^ _

 96..111   32..47    fehlen    ` a b c d e f g h i j k l m n o
112..127   48..63    fehlen    p q r s t u v w x y z { | } ~
    

Um ein Bild auszugeben muss es einen Bildspeicher haben, um die 24 Zeichenzeilen zu 40 Zeichen/Zeile = 960 Zeichen abzuspeichen. Wozu aber keine RAM Chips verwendet wurden, sondern 6 Stück Signetics 2504 Schieberegister vorhanden sind (je 2 an D4 und D5 und D14). Womit dies wegen nur 6 davon ein 6bit(!) Terminal ist, trotz 8bit Rechner. RAM steht für Random Access Memory, welches wahlfreien Zugriff auf beliebige Bits erlaubt, wozu RAM Chips Adressen brauchen. Schieberegister haben dagegen neben Stromversorgung und je eine Daten Ein und Aus Leitung nur noch einen Taktanschluss, aber keine Adressleitungen! Deswegen werden Bits von nur Ein genommen, und nach und nach intern durchgereicht, und erst an Aus ausgegeben nachdem alle anderen davor eingegangenen dort raus kamen. Was als Sequential Access Memory bekannt ist. Diese hier sind 1024bit lang, mit davon 960bit benutzt und 1024-960=64bit unbenutzt. Das ist der ungewöhnlichste Designansatz dieser Schaltung.

(Wie die VT05 übrigens auch, mit genau den selbigen nur 64 Zeichen. Aber jene hat 72x20=1440 Zeichen, von in 1970 mit 6 Serien von je 3 Stück * 512bit = 1536bit an Schieberegistern, mit 1536-1440=76bit unbenutzt. Auch Fernschreiber hatten oft nur selbige 64 Zeichen, wie beim ASR33. Weshalb die VT05 ebenfalls genau diese hat, und auch 72 Zeichen breit ist, weil als Fernschreiberersatz designt. Weshalb die 2513 ebenfalls diese drin hat, und somit auch der Apple 1 diese bekommen hat.)

Der DS0025 Chip (an C11 obere Hälfte) dient dazu, den Schieberegistern mit 2 Signalen ihren 4-phasigen Schiebetakt zu erzeugen, welcher pro "Umlauf" 2 Bytes durchschiebt, also pro Byte abwechselnd den einen oder den anderen braucht. Welches davon wird ausgewählt vom Q0 der 74160, welches bei jedem zweiten Zeichen eine 1 liefert. Gesteuert durch Signal "MEM 0" 0=aus/1=ein wird IN1 gepulst zu 1 wenn Q0=1 und "MEM 0" = 1, IN2 wenn Q0=0 und "MEM 0" = 1. Die OUT1 und OUT2 sind dann reversiert zu gepulst mit 0 und verstärkt und auf die Spannungslage der 2504er angepasst. Die 7427 (an C5) Logik davor erzeugt "MEM 0" = 1 wenn Signal "LINE 0" = 0 ist, was nur der Fall ist wenn beim D10 /HBL=1 ist (in den 40 Zeichen ausgeben), und Prozessortakt "CL" = 1 ist (in Pixelspalten 2..5 von 0..6, dieses "LINE 0" ist daher massiv missbenamst, es sollte "COLUMN 2 TO 5" heissen, da es diese 4 Spalten und nicht die erste Zeile des Zeichens anzeigt!).

Schieberegister können ihre Bits aber nur in einer Richtung schieben, diese alle pro Bild darstellen nur einmal durchgehend. Sie können dabei keine Wiederholungen im Bild liefern, aber 8 Videozeilen/Zeichen verlangen nach jeweils 8-facher Wiederholung der Zeichencodes! Daher ist nach den 2504ern noch ein Signetics 2519 40x6bit Hilfsschieberegister vorhanden (an C3), seine Bits einmal pro Videozeile durchgehend. Genauer mit Signal "LINE 7" (dieses ist richtig benamst) in der letzten Zeile von einem 8er Satz die nächste Zeile an von den 2504ern kommenden Zeichen aufnehmen (Pin RC=0 von "LINE 7" her), und danach im folgenden 8er Satz diese 8 mal wieder von sich geben (mit die ersten 7 mal wieder aufnehmen (Pin RC=1), aber beim 8ten mal die nächste Zeile).

Die Logik im C5 vor dem DS0025 dient auch dazu, mit "LINE 7" = 0 die 2504er nur in der letzten Zeile der Zeichen zu schieben, dann wenn die 2519 sie aufnimmt. Die 2519 selber wird nur vom Signal "LINE 0" geschoben, für einmal pro Zeichen in jeder Zeile. Wobei sie bei "LINE 0" = 1 getaktet wird, also beim Übergang von Pixel 5 nach 6 von 0..6. Wegen der langen Reaktionszeiten von 350ns bei der 2519 und 500ns bei der 2513 stehen die Pixel des nächsten Zeichens erst weit später bei der 74166 an, also kann diese trotz LD erst nach Pixel 6 aktiv sein immer noch die Pixel vom aktuellen Zeichen aufnehmen. Nach dem 40sten Zeichen liefert die 2519 bereits das erste Zeichen von der nächsten Zeile, aber wegen /HBL = 0 werden "LINE 0" und LD inaktiv. Nach dem horizontalen Strahlrücklauf wird mit /HBL = 1 wieder "LINE 0" die 2519 takten und mit LD endlich die seit 25 "Zeichen" wartenden Pixel aufnehmen.

(Genau diese Signetics 2504 und insbesondere 2519 Schieberegister Chips sind heute sehr schwierig zu bekommen, weil sie nur kurze Zeit hergestellt wurden. Die ganze Schieberegister Technik wurde erst anfangs 1970er eingeführt, als 512bit und dann 1kBit davon genug und schnell gross waren, aber 1kbit DRAMs entweder noch zu langsam (keine 1MHz Zugriffe) oder zumindest sehr aufwendig (komplexe Ansteuerung) waren, sowie 256bit SRAMs noch zu klein (32 Chips für 1kByte) und erst noch langsamer waren. Womit die Schieberegister schnell veraltet waren, sobald nur eine Chipgeneration später mitte 1970er die weit flexiblere RAM Technik genug schnell und einfach (4096 DRAMs mit 1MHz) sowie genug gross und noch einfacher (2102 SRAMs mit 1kbit) wurde. Der 2504 war noch recht verbreitet, und es gibt dazu kompatibel den National Semiconductor MM1404, aber die 2519 gab es nur von Signetics. Heute sind diese nur noch als Restposten zu erjagen, zu über $120 pro Stück! Ein einzelner Chip mit etwa 1/6 Anteil am etwa $700 Preis einer ganzen Mimeo 1, teurer als die Platine oder beide Trafos!)

(Das 2513 Font ROM ist dagegen einfach zu bekommen, weil weit länger benutzt worden (ist auch im DRAM basierten Apple II drin), sogar originale Signetics im originalgetreuen grauen Gehäuse. Wiederum sind unbenutzte 6301-1 PROMs nicht einfach, und die 4096 DRAMs auch nicht viel besser. Sie waren aber wenigstens auch von mehreren Herstellern, und wurden weit länger genutzt. (Weshalb Clone genau diese Terminal Logik komplett ersetzen durch Mikrocontroller. Ebenso neuere Speicherchips einsetzen. Ebenso die heute seltenen ASCII Tastaturen ersetzen durch PS/2 Tastaturen mit Mikrocontroller als Protokollwandler. Aber selbst manche Replikate werden mit PS/2 Tastaturen und externe Protokollwandler betrieben.))

Nach den 24*40=960 ausgeben, hat es noch die 1024-960 = 64 unbenutzten Bits der 2504er auszulassen, weil erst dann die ersten Daten von der letzten Bildausgabe wieder durchkommen um die Anzeige zu wiederholen! Diese werden nach dem Bild ausgeben im vertikalen Strahlrücklauf hindurchgetaktet. Genauer wurden bereits in der 8ten Videozeile der letzten Zeichenzeile schon 40 davon geholt. Aber es braucht trotzdem 64 holen, damit neben die weiteren 24 holen auch die ersten 40 von der ersten Zeichenzeile (vor-)geholt sind.

Bei genau 256-192=64 Zeilen davon könnte man hierzu etwas nehmen was 1 mal pro Zeile stattfindet, wie das Zeilenende. Aber die bestehende Logik schiebt die 2504er nur alle 8 "LINE 7" Zeilen, weshalb man auf 8 mal pro Zeile kommen muss. Die bestehende Logik tut aber 40 mal pro Zeile schieben, also ist 40-8 = 32 davon eliminieren angesagt, also 32/40 oder 4/5. Die einzige weitere auf das Signal "MEM 0" einwirkende Logik vom 7402 (an C10) wird bei Signal "/VBL" im Rücklauf aktiv, und testet das Q3 der 17160, welches wegen 0..9 zählen 8 mal 0 und 2 mal 1 ist. Damit werden 2/10 und somit 1/5 der 40 bestimmt, was bei 8 Zeichen 08,09,18,19,28,29,38,39 shiftet, und zusammen mit 64/8=8 Zeilen genau auf 8*8=64 Shifts kommt! Dies so einfach zu machen ist wohl der Grund, warum es nur 40x24=960 Zeichen und 64 unbenutzte sind statt 40x25=1000 und 24, trotz dass letzteres noch in die 1024bit der 2504er passen würden.

Der erstaunlichste Nebeneffekt der Schieberegister ist aber, dass sie nur beim Auslesen bzw wieder vorne Einlesen beschreibbar sind, indem an der aktuellen Anzeigeposition statt einer Kopie des alten Inhaltes ein neuer eingespeichert wird. Dies mit zwei 74157 (an C4 und C14) als "alt/neu" Schalter (wie er in der 2519 bereits eigebaut ist). Daher kann die Terminal Logik nur einmal pro Bild ausgeben ein Zeichen einfügen. Womit es unmöglich ist, bei NTSC 60Hz (= 60 Bilder/Sekunde) mehr als 60 Zeichen/Sekunde auszugeben, 2/3 Sekunde pro 40er Zeile. Die ganzen max 960 Zeichen ausgeben dauert daher max 960/60 = 16 Sekunden!

(Eine zweite 2519 vor den 2504ern könnte dies um bis zu Faktor 40 beschleunigen, bzw genauer um soviele Zeichen wie die ausgegebenen Zeilen durchschnittlich darin haben. Dies weil mit bis zu 15750 Zeichen/Sekunde aufnehmen, und dann mit bis zu 60 ganze Zeilen/Sekunde weitergeben. Soweit ich weiss, hat die VT05 so etwas drin.)

(Wozniak hat nach manchen Aussagen diesen Schieberegister Ansatz vom Don Lancaster seinen TV Typewriter von 1973 übernommen. Er selber hat dies aber gegenüber dem Mimeo 1 Designer Mike Willegal verneint: Er habe dieses Projekt gar nicht gekannt, und man könne das problemlos erkennen durch die beiden Schaltungen vergleichen und Differenzen erkennen. Der ganze Video Teil sei als TV Terminal designt worden, während TV Typewriter zuerst nur Editor war, mit Terminal Option. Ziel war an ein 300Baud Modem, welches nur 30 Zeichen/Sekunde liefern kann, daher war schneller sein hier unbedeutend. Erst danach wurde der lokale Rechner addiert, wonach langsam sein nervte. Aber die Antwort darauf war der Apple II, mit einiges weniger Videologik und trotzdem 16facher Geschwindigkeit. Die ganze Zählerkette wurde von seinem ehemaligen Arbeitsstelle übernommen, Videospielautomaten bei Atari. Wegen Terminal sein hat es auch 40x24 in 1kbit Schieberegister statt der TV Typewriter ihre 32x16 Zeichen in 512bit, und auch die 40x6bit 2519 Hilfsschieberegister statt die kleineren 32x6bit 2518, sowie die von 960 auf 1024 Zeichen 64 unbenutzte überspringen Logik, welche die TV Typewriter gar nicht braucht. Weiter besagt er, dass er auf Platinenflaeche statt Chipzahl optimiert habe. Vermutlich hat er daher auch 8pin 1024bit Schieberegister benutzt, statt 16pin 1k SRAMs wie Sol-20 und PET 2001 und TRS-80 Model I.)

Die Zeichen selber kommen von der PIA 6820 her, als Signale "RD1..7" welche die ASCII Bits 0..6 darauf haben. Die Handshake Signale "DA" und "/RDA" zeigen "Zeichen wartet" bzw "Zeichen verarbeitet" an. Eine 74174 (an C7) synchronisiert die beiden mit den Schieberegistern, mit D1 zu Q1 bzw D2 zu Q2. Diese wird mit dem Signal "MEM 0" getaktet, und damit nur bei Zeichen an die 2519 übergeben und die 2504 bewegen aktiv. Die 7410 (an C6) mit Signal "CURS" wartet bis diese bereit sind, in der richtigen Position geschoben sind um dies zu machen, wenn angezeigtes Zeichen = Zeichen an Cursorposition. Die 7427 (an C5) erkennt mit RD6=1 oder RD7=1 ob es ein druckbares ASCII 32..127 Zeichen ist. Die 7432 (an C9) schaltet mit Signal "/WRITE" die beiden 74157, von "alt" (= behalten) zu "neu" (= einfügen).

Ausnahme gilt nur für das "neue Zeile" Steuerzeichen, ASCII 13 = Carriage Return (ohne ASCII 10 = Line Feed danach), welches einfach die restliche aktuelle Zeile mit Leerzeichen auffüllt, in einem Zug, mit danach automatisch am Anfang der nächsten Zeile landen! Dieses wird von der restlichen Logik rechts von der 74174 erkannt, und zusammen mit der 74174 gesteuert. Die Logik aus den Signalen "RD1,3,4,2,5", mit selbiger Synchroniation und Zeichenposition, plus obiges "druckbares Zeichen" nicht der Fall sein, erkennt den Anfang vom Carriage Return. Die D3 zu Q3 Speicherung hällt dieses aber danach aufrecht, um Leerzeichen zu wiederholen. Das Signal "LAST H" via D4 zu Q4 unterdrückt dann D5 zu Q5, und beendet somit weiter wiederholen.

Die Cursorposition wird, um Bauteile zu sparen, mit einem wandernden Bit in einem siebenten 2504 (an C11) gespeichert. Dieses verwaltet von zwei Viertel vom 74175 (an C13) welche es normalerweise durchlassen, aber bei Zeichen ersetzen leicht verzögern, bzw bei Zeilenende bis in die nächste Zeile verzögern. Dies mit einem Teil vom einten 74157 (an C14) welche es an der alten Position löscht. Das Signal "/WC1" entsteht egal ob "/WRITE" oder "neue Zeile", und wird von der 74174 um ein Zeichen verzögert zu "/WC2", welches mit dem 7408 (an D12) die neue Cursorposition setzt, nachdem "/WRITE" in der 74157 den alten "CURS" durch 0 ersetzte/löschte. Dies ist eindeutig das eigenartigste Stück Hardware im ganzen Rechner! (Ich bin nicht sicher, ob ich diesen Mechanismus komplett durchschaut habe.)

Obiges reicht eigentlich wenn ein Carriage Return am Ende einer normalen Bildzeile ausgegeben wird. Aber falls der Cursor auf der untersten ist, was hier ohnehin immer der Fall ist, würde er am Anfang der nicht mehr vorhandenen nächsten Zeile zum Bild herausfallen. Mit der 74174 vom Signal "MEM 0" getaktet würde er somit nach vertikalem Strahlrücklauf abwarten einfach am Anfang der ersten Zeile wiedererscheinen, so das Bild zyklisch überschreibend! Statt dessen ist hier aber scrollen vom Bild erwünscht. Dies kann man wegen der Shiftregister nur durch weitere 40 Shifts erzeugen machen, die erste Zeile ihre 40 Zeichen verlierend und die neue Zeile ihre 40 zu Leerzeichen machend. (Der TV Typewriter hat dagegen genau solches zyklisches Bild überschreiben.)

Aber es wird bereits in allen Zeilen geshiftet, 40 mal beim zeichnen, bzw nur 8 mal im vertikalen Rücklauf. Also kann man weitere 40 Shifts nur bekommen, indem man eine Zeile mehr zeichnet und taktet! Dazu wird, wenn das Signal "/VBL" inaktiv wird, nach dem Schritt von Zeile 191 zu 192, und nur falls ein Carriage Return am füllen ist (oder ein normales Zeichen die Zeile auffüllt), was per Signal "/WC1" angezeigt wird, die Zeilennummer auf 191 zurückgesetzt. Womit erneut die letzte Zeile der Ausgabe ist, und somit nochmals die 2519 mit 40 Zeichen eingelesen wird für die nächste Zeile, womit die bereits geholte erste vom Bild durch nochmals holen überschrieben und somit fallen gelassen wird!

Dazu wird ausgenutzt, dass die beiden 74161 sonst 0..255 laufen, ohne mit PE zu laden, was nun für das 191 laden benutzt wird. (Was ich hier nicht verstehe ist, warum die 191 auch auf 0 schaltbar ist, aber das nur wenn nicht im vertikalen Strahlrücklauf oder wenn die 74160 Q3=1 hat, also auf 8 oder 9 ist. Wobei ersteres aber Bedingung ist um die 191 zu laden, ausser dies ist nur ein Seiteneffekt von der 7402 (an C10) als reinen Inverter von Q3 doppelt nutzen. Aber zweiteres macht auch keinen Sinn, warum es überhaupt dann 0 statt 191 laden soll, zumal bei 192 werden die 74160 von Signal "LAST H" neu 5 hat, und bis sie bei 8 ankommt die 74161 bereits wieder bei 191 sind.)

Selbiges füllen für längere Zeit machen ist auch die Bildlösch Logik, welche so lange wirkt wie die Bildlösch Taste auf der Tastatur gedrückt wird. Dabei wird einfach alles mit Leerzeichen überschrieben. Dabei wird ebenfalls die Zeilenzahl, sobald vertikaler Strahlrücklauf erreicht ist, mit dem Signal "/WC1" auf 191 gehalten bis losgelassen, gefolgt von mit Signal "/WC2" den Cursor setzen. Dazu wird Signal "LAST" statt "LAST H" benutzt, welches garantiert dass mindestens bis zu einem Bildende gelöscht wird. Daher wird der Cursor auch auf die neue Zeile nach der letzten gelöschten gesetzt, womit dieser stets auf der untersten Zeile zu stehen kommt. Wonach die erste Ausgabe und Eingabe zu unterst im Bild geschieht, nicht von oben nach unten bis Bild voll, mit ab dann auf der untersten bleibend. Was vom Verhalten her einem Papier Fernschreiber oder einer Schriebmaschine entspricht. Womit dies aber auch praktisch ein voller Reset der Terminal Logik ist.

(Wer an einem Replikat eine Apple II Tastatur benutzt hat keine Bildlösch Taste darauf. Das ist genau der Unterschied, welcher diese zu "sehr ähnlich" aber nicht ganz identisch macht. Diese Taste muss man daher extern in der Tastaturleitung einflicken. Clone haben Reset und Clear Tasten auf ihrer Platine, oder lassen gar den PS/2 Tastatur Mikrocontroller auf Hotkeys reagieren.)

Die eigentlichen Leerzeichen bei Carriage Return und Bildlösch werden von den beiden 74157 "alt/neu" Schaltern "nebenbei" erzeugt, indem einfach ihre Ausgänge per Signal "CLR" auf 0 gezogen werden. Diese 0 kommt sowohl in die 2504er wie auch direkt in den 2519. Daher wird auch das ASCII Bit6 erst beim kopieren von den 2504 zum 2519 invertiert fürs 2513 ROM, mit dem 7402 (an C10). In den 2504 ist Bit6 noch direkt drin, damit dort eben 0 = Leerzeichen gilt.

Beim invertieren können gleichzeitig mit einer 7408 (an C12) alle angezeigt werdenden Zeichen mit Bit6=0 (also Leerzeichen und Sonderzeichen) ohne die 2504 zu ändern mit Bit6=1 (zu @ bzw Buchstaben) modifiziert werden, falls sie an der Cursorposition sind und die 555 (an D13) 1 abgibt. Damit entsteht vom 555 gepulst an der Cursorposition, welche stets ein Leerzeichen hat, ein blinkender Leer/@/Leer/@/... Cursor. Es kann kein reversblinkender Cursor dargestellt werden, weil die 2519 kein siebentes Bit speichern kann, um die "zeige Cursor" Position separat von der 6bit Zeichennummer zu halten. (Im Handbuch steht dazu nur, dass es einen blinkenden Cursor hat, aber nicht wie er aussieht. Dass er somit ein blinkendes @ ist hab ich erst aus der Schaltung analysieren herausgelesen. Elektronik lesen macht Spass!)

Da einfügen alles weit langsamer ist als der Prozessor arbeiten kann, muss dieser bei jeder Ausgabe nachprüfen ob die Terminal Logik das letzte Zeichen bereits verdaut hat, und ein neues annehmen kann, oder der Prozessor warten muss. Die PIA 6820 verwaltet diesen Status nebenbei, mit dem Handshake, setzt via Pin CB2 das Signal "DA" wenn sie vom Prozessor ein Zeichen bekommt, und meldet diesem "erledigt" wenn sie das Signal "RDA" vom 74174 auf Pin CB1 bekommt. Selbiges macht sie auch für die Tastatur, Status ob bereits eine Taste vorliegt, oder der Prozessor warten muss, mit Signal "STB" von der Tastatur auf Pin CA1.

Ein letzter Nebeneffekt davon, dass Schieberegister nur in einer Richtung schieben können, ist dass diese Cursorposition nicht reversiert werden kann, die Terminal Logik daher eine reine Fernschreiber-artige Ausgabe hat. Womit es auch kein überschreiben oder gar korrigieren ausgegebener Zeichen geben kann. Bei Backspace kann der Rechner zwar im RAM ein Zeichen löschen, aber nicht auf dem Bildschirm! Wer jetzt das Bild ganz löschen und neu ausgeben will, stellt dazu noch fest, dass es dafür gar kein Steuerzeichen dafür gibt, oder überhaupt irgendwelche Steuerzeichen ausser Carriage Return! Was drausen ist bleibt draussen, bis es weggescrollt wird. Was der Rechner nur durch durch 24 Carriage Return schicken machen kann, und 24/60 = 2/5s dauert. Oder die Bildlösch Taste auf der Tastatur gedrückt wird, wovon aber wiederum die Software nichts mitbekommen kann, genauso wie ein Fernschreiber oder Nadeldrucker von einem Blatt Papier manuell vorspulen nichts merkt!

(Die VT05 hat trotz ähnlicher Logik sowohl einen reversierbaren (und sogar beliebig bewegbaren und voll positionierbaren) Cursor, wie auch ein Bild löschen (und auch aktuelle Zeile löschen) Steuerzeichen. Ebenso die TV Typewriter, welche ja als Texteditor designt wurde. Es geht also prinzipiell. Bild löschen wäre eigentlich nur nach dem "Steuerzeichen oder Textzeichen" Test falls es Steuerzeichen ist neben auf Carriage Return auch auf z.B. Form Feed testen (VT05 testet auf $1F für Bild löschen und $1E für Zeile löschen), und mit dem Testergebnis mindestens 1 Bild/Zeile lang den Bildlösch Taste simulieren. Aber dies wurden hier wegen Fernschreiberersatz Terminal und dann Minimalrechner eingespart, womit diese Terminal Logik sogar noch weniger Funktionsumfang hat als ein VT05 Terminal oder TV Typewriter!)

Apple II im Vergleich

Als Vergleich dazu, hat der Apple II selbige 6502, selbige 1.0227MHz, von selbigem 14.31818MHz Quarz, auch durch 14 geteilt. Ein originaler Apple II hat ebenfalls keine Power-On Reset Logik (ein Apple II Plus hat aber solche). Dazu kommen bis zu 3 statt 2 Bänke DRAM, aus entweder 8 Stück selbiger 4096 Chips (= 4kByte), oder 8 Stück neuerer 4116 (= 16kByte), oder gar Bänke beider Sorten im Rechner gemischt, und somit zu 4 8 12 16 20 24 32 36 oder 48kByte kombinierbar. Mit der Language Card kann eine weitere Bank von 16k mit 4116er addiert werden.

Weiter hat es bis zu 6 Stück 2kx8bit ROMs, somit bis 12kByte. Diese mit massiv erweitertem Monitor, mit bereits eingebauter Kassettenband Software, im obersten der 2k (Apple II hat Monitor II, und Apple II Plus hat Autostart Monitor). Ebenso gibt es erweitertes Basic im ROM (Apple II hat Integer Basic in 3*2=6k, und Apple II Plus hat Applesoft II Basic (ein Microsoft Basic mit Floating Point) in 5*2=10k). Vor der Language Card gab es noch die Firmware Card, mit der man beide Softwares als ROMs umschaltbar haben konnte (eines im Rechner, anderes auf der Karte), später wurde die Language Card für zweite oder anderes in Karten-RAM geladen benutzt (benamst ist sie weil für andere Sprachen als Basic gedacht, anfangs diskbasiertes Pascal).

Die 74154 und Lötpunkte sind durch aufwendigere flexiblere Logik und Steckplätze ersetzt. Diese kann mit 2 74139 Chips 7 Orte für 4k DRAMs an 0xxx 1xxx 2xxx 3xxx 4xxx 5xxx oder 8xxx, bzw 3 Orte für 16k DRAMs an [0-3]xxx [4-7]xxx oder [8-B]xxx decodieren. Danach mit Steckplätzen statt Lötpunkten, um die Bänke den Orten zuzuordnen, mit für alle typischen Konfigurationen fertige Steckmodule angeboten (und im Handbuch eine Anleitung um untypische selber herzustellen). Dies ergibt alle DRAMs in Adressen 0xxx..Bxxx. Dazu kommen mit 1 74138 Chip ROMs in Dxxx..Fxxx (wovon Monitor in F800..FFFF, und Basic in E000..F7FF bzw D000..F7FF), und Ein-/Ausgabe im folglich verbleibenden Cxxx (Strikte ist Ein-/Ausgabe C000..C7FF, mit C800..CFFF für ROM oder RAM auf IO Karten). Die Language Card addiert ihre 16k als 12k in [D-F]xxx, plus 4k als doppelte Dxxx, weil nicht in Cxxx.

Video ist selbiges Schwarzweiss ohne Graustufen, selbige 40 Zeichen/Zeile zu 24 Zeichenzeilen = 960 Zeichen. Das mit selbigem 5+2=7 Pixel in 7+1=8 Videozeilen pro Zeichen, aus selbigem 2513 Font ROM und somit selbigem 5x7 Font in 7x8 Feld. Ergibt insgesammt selbige 280x192 Pixel, ebenso "breite" Pixel und streifiges Bild. Also selbige Eckdaten wie der Apple 1, abgesehen von mehr Speicher, passend zu einem revidierten und erweiterten Design.

Komplett anderst gestaltet ist aber die Videoausgabe. Es fehlen alle Schieberegister! Es hat nicht mal SRAMs als deren Ersatz. Das Bild wird direkt aus dem DRAM Hauptspeicher geholt, aus einem 1k Ausschnitt davon. Dabei sind die 40 Zeichen einer Zeile linear hintereinander, aber die 24 Zeilen sind nichtlinear, in 8 Blöcken zu je 3*40+8=128 Bytes, mit Zeilen in der Reihenfolge 0,8,16 1,9,17 1,10,18 .. 7,15,23. Daher hat es ebenfalls nur 40x24=960 Zeichen sowie 8*8=64 unbenutzte Bytes, trotz dass wieder eigentlich 40x25=1000 mit 24 unbenutzte Bytes in 1k passen würden.

All dies bremst den Prozessor nicht mal, weil die neueren 4096 bzw 4116 DRAM Chips sogar 2MHz aushalten, dem Prozessor und Video je 1MHz davon geben. Auch der Refresh bremst den Prozessor nicht mehr auf 0.960MHz, da es im Video seiner Zeit gemacht wird, bzw die ohnehin notwendigen Video Speicherzugriffe liefern dank nichtlinearer Speicheradressierung gleich den Refresh mit!

Weil das DRAM 8bit breit ist, die 2513 wegen ihrer 64 Zeichen aber weiterhin nur 6bit ausnutzen kann, schalten die verbleibenden 2bit zwischen normaler reverser oder blinkender Darstellung. Mit letzerem von selbigem 555 Chip gesteuert. Das wird dann für das Zeichen mit den Cursor blinken benutzt, diesmal echtes reversblinken, und somit auch bei allen Zeichen benutzbar, nicht nur bei Leerzeichen mit zu @ blinken.

Es gibt zudem zwei Graphikmodi, "Low Resolution" mit nur 40x48 Blöcke aber in 16 Farben, aus selbigen 960 Bytes von 1k, sowie "High Resolution" mit 280x192 Pixel Bitmap aber nur in 6 Farben, aus 8*960 Bytes von 8*1k=8k. Zudem kann man 20*8 Zeilen oben Graphik mit 4*8 Zeilen unten Text mischen. Ebenso kann man 2* 1k bzw 2* 8k Bytes an Speicherausschnitte umschalten, damit es Speicherplatz für zwei Bildseiten hat, um bessere Animationstechniken zu erlauben. (Der Apple II Europlus kann neben NTSC 60Hz auch PAL 50Hz Timing. Aber die Farben funktionieren auf PAL Farbmonitor oder Fernseher nicht, weil es dazu 17.734472MHz statt 14.31818MHz als Taktbasis bräuchte.) (Der Apple 1 wurde anfangs auf mit Farben erweitern ausgelegt, daher auch die 14.31818MHz benutzt. Aber bis es soweit kam wurde der Apple II wichtiger, und bekam die Farben.)

Der Prozessor beschreibt den Bildspeicher direkt, wie auch den restlichen Speicher, von dem der Bildspeicher nur ein Ausschnitt ist, weshalb dies ein echter Homecomputer ist. Alle Schieberegister Probleme sind weg. Nichts mehr mit langsamer 1 Zeichen/Bild 60 Zeichen/s Ausgabe, diese ist nur noch durch mit den Prozessor das Bild scrollen umkopieren auf etwa 1000 Zeichen/s limitiert, eben 16fache Geschwindigkeit. All das ohne unbenutzte Bits durchtakten. Auch ohne Carriage Return auffüllen Logik, und ohne beim Scrollen Zeilen wiederholen. Ebenso kann er Cursorposition reversieren oder gar beliebig positionieren, und somit auch Bild löschen oder gar korrigieren/editieren/animieren. Ebenso weg ist die PIA 6820, sowie bei Ausgabe auf deren Status warten.

Aber auch weg ist die automatische ASCII Zeichenverarbeitung der Terminal Logik, sowie automatisches löschen und scrollen, sowie automatisches Cursor positionieren. Dies alles muss nun von der Systemsoftware im Treiber simuliert werden, welcher ebenfalls im grösseren Monitor ROM drin ist, und dort 200 Bytes braucht, statt Apple 1 die 6820 ansteuern in 9 Bytes. Diese geht sogar soweit, den Cursor nach Bild löschen auf die unterste Zeile zu setzen, wie im Apple 1. Sie liefert aber auch mehr Funktionalität. Wegen dem gemischten oben Graphik und unten Text kann der gescrollt werdende Bildspeicherausschnitt reduziert werden. Diese Funktion wurde gleich auf alle vier Bildkanten ausgeweitet, was beliebige scrollbare Fenster erlaubt, um Titel-/Fusszeilen oder seitliche Menus stehenzulassen.

Tastatur ist eine sehr ähnliche, selbiges direkt ASCII erzeugend, nur ohne der Bildlösch Taste (das muss nun auch die Software machen). Sie wird aber gleich mitgeliefert. Sie wird neu ebenfalls ohne 6820 gelesen, mit nur 2 74LS257 und dem Status in 1/2 74LS74. Dabei ging aber auch die Möglichkeit von Tastatur von IRQA/KYBD an IRQ legen verloren, weshalb er trotz mehr Monitor weiterhin keine Tasten/Zeichen vorzu in einen Tastenbuffer lesen sein kann, er solche nur sehen kann wenn auf Eingabe wartend. Weiterhin hat die Tastatur wegen nur leicht erweitertes ASCII erzeugen unvollständige Pfeiltasten (links/rechts aber kein auf/ab, und links ist nur Backspace, ist also eher eine Korrekturtaste). Zudem kann die Kommandozeileneditor Software auch nicht den Cursor positionieren, was auf frei bewegendem Cursor aufbauenden Komfort praktisch verhindert. Selbst Apple II Fans erachten dessen Kommandozeileneditor als ein schwaches Feature.

Slots sind bereits 8 eingebaut, mit Nummern 0..7, ohne Erweiterungsplatine. Die 1..7 sind auf je eine Page bei Adressen C1xx..C7xx fest verdrahtet für PROMs darauf, sowie alle 8 zudem auf 16er Sätze C08x..C0Fx für Ein-/Ausgabe darauf. Des weiteren sind C800..CFFF frei für schaltbare grosse ROM oder RAM auf Karten. Dazu kommt noch, dass das Kassetten Interface bereits eingebaut ist. Zudem hat es einen Beep Tongenerator, sowie einen 4Paddle/2Analogjoystick Anschluss, weil Wozniak an Breakout-artigen Spielen interessiert war (er hatte für Atari am Breakout Videospielautomaten optimieren gearbeitet). Alle eingebaute Ein-/Ausgabe ist in C00x..C07x, mit als Teil davon auch die Videomodi und Bildseiten schalten.

Schliesslich hat es mitgeliefertes Schaltnetzteil und auch Gehäuse, und dies alles ist fixfertig zusammenmontiert. Nur einen normalen NTSC Videomonitor oder Fernseher in Schwarzweiss oder Farbe sowie einen normalen Kassettenrekorder musste man haben oder separat besorgen. Dafür kostete er auch $1300, statt den $666 vom nackten Apple 1. (Hier gab es aber, analog zur seltenen blanken Platine Ausgabe der Apple1, auch eine seltene nackte Platine Ausgabe, welche $800 kostete. Diese war vor allem als Upgrade für bestehende Kunden gedacht, welche schon eine Tastatur hatten.)

(Gemäss manchen Aussagen war dieses revidierte Design die Folge davon, dass der Apple 1 Designer Steven Wozniak diesen dem 6502 Prozessor Designer Chuck Peddle vorführte. Wonach jener statt Lob einen Zusammenschiss erteilte, dass er keine Ahnung habe, wie man mit Mikroprozessoren designt. Gefolgt von einer Lektion wie man es macht, dedizierte Hardware wie im Apple 1 ersetzen durch Prozessor und Software. Mit dem Apple II habe Wozniak diese Lektion voll umgesetzt, und dazu noch massiv erweitert, während Chuck Peddle seine Vorstellung mit dem PET umsetzte. Andere Aussagen besagen aber, dass Wozniak den Apple 1 bereits wegen Geschwindigkeit und Chips sparen verbesserte, und erst den Apple II Prototyp Chuck Peddle vorführte, als Teil eines Versuches diesen an Commodore zu verkaufen, der scheiterte weil Steve Jobs zu gierig war. Chuck Peddle selber sagte dazu in einem Interview, er habe bei einer Werbetour um den 6502 zu verkaufen von zwei in einer Garage gehört, welche Hilfe brauchen um die 6502 zu benutzen, habe diese besucht und geholfen. Was eher zur Apple 1 als zur Apple II Situation passt. Weitere Aussagen besagen, dass Chuck Peddle den PET als Fortsetzung der TIM Serialterminal und KIM-1 Hexkit Rechner ausdachte, die zwecks Vermarktung des 6502 Prozessors und als Entwicklungssysteme entwickelt wurden. Wonach er das PET Konzept sogar erfolglos an Tandy zu verkaufen versucht hat, noch bevor er wegen dem Aufkauf von MOS Technology durch Commodore dort landete und Jack Tramiel davon überzeugte. Mit dann nach obigem Apple II Verkauf scheitern den PET umsetzen. Die Fortsetzung könnte auch erst nach den Apple 1 sehen geschehen sein, aber sicher nicht nach Apple II sehen. Noch weitere Aussagen besagen, dass Tandy Designer nach sehen wie einfach der PET war den TRS-80 entwickelten und dann intern verkaufen konnten. Womit alle 3 Rechner des "Trio von 1977" mit einander verwandt sind!)

(Der PET 2001 ist noch konsequenter in Software statt Hardware. Zeichen hat es statt 2513 Font ROM mit 64 Zeichen in 7x8 Feld, ein normales 2kx8bit ROM mit eigenen 2 Fonts zu je 128 Zeichen in 8x8 Feld. Das erlaubt umschaltbar teil ASCII 32..63 plus 64 Graphikzeichen (inklusive 2x2 Blockgraphik), oder voll ASCII 32..127 plus 32 Graphikzeichen (inklusive der Blockgraphik). Es bleibt nur noch 1bit für normale oder reverse Darstellung, mit daher Cursor blinken in Software statt mit 555. Es hat keine Graphikmodi, kann Low Res aber teilweise mit Blockgraphik Zeichen kompensieren (doppelte Blockzahl aber keine Farben), aber hat keinerlei Bitmap wie bei High Res (und auch keine Farben). Bild kommt aus einem separaten 1k Bildspeicher (2 2114 SRAMs) statt einem 1k Ausschnitt vom Hauptspeicher, somit auch keine zwei Bildseiten. Dafür aber linear angeordnet, mit vollen 40x25=1000 Zeichen und nur 24 unbenutzte Bytes. Zusammen mit 8x8 Font in 8x8 Feld und somit 320x200 Pixel (und mit der 2x2 Blockgraphik im Font 80x50 Blöcke). Allerdings hat er auch bloss 4oder8k Hauptspeicher (4oder8* 2 2114 SRAMs), daher nur soviel Platz wie der Apple 1, trotz selbiger Herstelltechnologie Generation wie die 16k DRAMs im Apple II. (Nachfolger CBM 3000 hat 16oder32k DRAM (1oder2* 16k 4116 DRAMs).) Die Tastatur erzeugt nicht ASCII per Spezialchip, sondern arbeitet mit der Tastenmatrix direkt vom Prozessor einscannen und in Software zu ASCII verarbeiten. Sie kann daher auch vollen Satz Pfeiltasten anbieten. Dazu erst noch mit einem generischen Bildrücklauf Zeitpuls von der Videoausgabe an den IRQ genutzt, um zu scannen und in einem Tastenbuffer ablegen. Ebenso hat die Ausgabe Cursor beliebig positionieren, und der sehr komfortable Kommandozeileneditor kann damit direkt im Bildspeicher herumeditieren. Diverse Commodore Fans geben dessen Kommandozeileneditor als das beste Feature vom ganzen Rechner aus. Mit dafür aber noch mehr Systemsoftware im Treiber von dessen Monitor ROM. Insgesammt hat es 2* 2kx8bit Monitor und 1* 2kx8bit Treiber/Editor und 4* 2kx8bit Basic (neben obigem 1* 2kx8bit Font) an ROMs drin. Der Videomonitor und ein Kassettenrekorder sind bereits eingebaut, er kann so aber keine bestehenden benutzen/teilen. Er kostete aber trotzdem nur $600 oder $800, je nach ob 4k oder 8k.)

Kommandozeilen Interface

Das ist die Apple 1 Hardware. Aber wie ist der zu benutzen? Was kann die Systemsoftware darauf, welche in nur 256 Bytes an PROM passen muss? Eigentlich alles was man braucht, und mit einem Maschinencode Monitor Programm üblicherweise machen kann, Daten und Programme anzeigen, ebensolche eingeben und modifizieren, sowie Programme starten! Aber auch alles sehr primitiv und minimalistisch. Für die vollen Details siehe das Apple 1 Handbuch, Seiten 3 und 4.

Einschalten (bzw erst die Reset Taste auf der Tastatur) tut genau einen Backslash ausgeben als "Abbruch" Zeichen (eben weil kein löschen im Terminal existiert), und dann die Cursorposition auf die nächste Zeile setzen. Einen Prompt gibt es keinen, nur den Leer/@/Leer/@/... blinkenden Cursor. Auf der Kommandozeile kann man eintippen, maximal 128 Zeichen. Bei mehr bricht er mit automatisch obigem Backslash ausgeben und zu nächste Zeile gehen ab, was praktisch einen Softreboot des Rechners entspricht. Dabei gehen keine Daten/Programme vom Speicher verloren, aber die aktuelle Eingabezeile komplett. (Dem Apple II sein massiv erweiterter Monitor II gibt ein "*" als Prompt aus, und hat maximal 255 Zeichen.)

Mit ASCII "_" (auf alten ASCII 1963 kompatiblen Tastaturen ist das ein Pfeil nach links, zum "_" wurde es erst mit ASCII 1967) kann man ein Backspace/Backdelete machen. Im Speicher der Kommandozeile wird ein Zeichen gelöscht, aber auf der Anzeige bleibt das Zeichen erhalten, bzw genauer wird dahinter auch noch das "_" ausgegeben. Mit ASCII Escape kann man "Abbruch" verlangen, wieder gibt der Monitor einen Backslash aus. ASCII Carriage Return veranlasst die Kommandozeile auszuwerten.

Kommando Ausgabe

Bei als Kommando einfach eine Hexzahl (aus Ziffern 0..9, A..F) eintippen, werden dessen letzten 4 Ziffern als eine Adresse verwertet und diese gefolgt von : und ihren Inhalt in Hex ausgegeben. Zahlen mit 1..3 Ziffern werden mit 0-en davor auf 4 Ziffern aufgefüllt. Tippt man Mist kann man statt "Abbruch" einfach 4 weitere richtige Ziffern eingeben, alle davor gehen dabei verloren, weil "vorne" herausgeschoben, aber mit "_" Backspace/Backdelete ist oft schneller. Mehrere Adressen mit Leerzeichen getrennt gelten als mehrere Kommandos, und erzeugen mehrere Zeilen mit je Adresse und Daten. So wie hier gezeigt:

Benutzer:   FF00
Rechner:    FF00: D8
Benutzer:   2B
Rechner:    002B: 00
Benutzer:   28002B
Rechner:    002B: 00
Benutzer:   28_B
Rechner:    002B: 00
Benutzer:   FF00 FF10 FF20 FF30
Rechner:    FF00: D8
Rechner:    FF10: DF
Rechner:    FF20: 8D
Rechner:    FF30: D0
    

Kommando Blockausgabe

StartAdresse.EndAdresse gibt Startadresse und alle Daten in diesem Adressbereich aus. Diese als maximal 8 Bytes pro Zeile, was 4 Adresse + 1 ":" + 8 * (1 Leer + 2 Daten) = 29 Zeichen pro Zeile an Platz benutzt, für soviele Zeilen wie nötig. Was wegen 29 Zeichen plus Carriage Return ganze (29+1=30)/60Hz=0.5s pro Zeile dauert, nicht unterbrechbar ausser mit Reset Taste. Nur .EndAdresse gibt aber nach der letzten ausgegebenen Adresse fortgesetzt aus, und erlaubt so portionenweise ausgeben. Wieder sind mehrere Adressen und/oder StartAdresse.EndAdresse beliebig gemischt mit Leerzeichen getrennt erlaubt. Damit kann man beliebige Daten/Programme anzeigen. Das Monitorprogramm selber kann mit FF00.FFFF ausgegeben werden. Das ist aber 32 Zeilen zu 8 Bytes, passt nicht voll auf den Bildschirm, weshalb man eher FF00.F7FF und danach .FFFF benutzen wird, was dann zweimal 16*0.5=8 Sekunden dauert. Hier nur einen Ausschnitt davon ausgeben:

Benutzer:   FF00.FF1F
Rechner:    FF00: D8 58 A0 7F 8C 12 D0 A9
Rechner:    FF08: A7 8D 11 D0 8D 13 D0 C9
Rechner:    FF10: DF F0 13 C9 9B F0 03 C8
Rechner:    FF18: 10 0F A9 DC 20 EF FF A9
Benutzer:   .FF3F
Rechner:    FF20: 8D 20 EF FF A0 01 88 30
Rechner:    FF28: F6 AD 11 D0 10 FB AD 10
Rechner:    FF30: D0 99 00 02 20 EF FF C9
Rechner:    FF38: 8D D0 D4 A0 FF A9 00 AA
Benutzer:   FF00.FF07 FF10 FF20.FF23 FF34.FF35
Rechner:    FF00: D8 58 A0 7F 8C 12 D0 A9
Rechner:    FF10: DF
Rechner:    FF20: 8D 20 EF FF
Rechner:    FF34: 20 EF
Benutzer:   .FF3F
Rechner:    FF36: FF C9
Rechner:    FF38: 8D D0 D4 A0 FF A9 00 AA
    

Die ganzen 32*8 = 256 Bytes ohne Eingaben hab ich in einem separaten Hex Dump File im Apple 1 Ausgabeformat ausgegeben.

(Den ganzen Speicherbereich mit 0000.FFFF ausgeben braucht übrigens 8192*0.5=4048 Sekunden, 68 Minuten, über 1 Stunde! Dies wurde an der VCFB 2019 Ausstellung (in Berlin) ausgenutzt um mit minimalstem Aufwand den Rechner aktiv zu präsentieren.)

(Apple II tut bei leerem Return einfach die nächste einzelne Zeile als Block ausgeben. Ein Apple II Plus mit Autostart Monitor kann durch Ctrl-S (Stop) eingeben eine zu lange Ausgabe pausieren.)

Kommando Eingabe

Bei als Kommando StartAdresse: Daten ... eingeben, mit die einzelnen Datenbytes durch Leerzeichen getrennt, gibt es zuerst den alten Inhalt der StartAdresse aus und überschreibt dann diese, und auch alle folgende Adressen ohne weitere Ausgaben. Wieder gilt 1 Ziffer wird mit 0 auf 2 aufgefüllt. Ebenso bei Mist tippen einfach 2 richtige Ziffern eingeben, die davor gehen "vorne" herausgeschoben vergessen. Ebenso wirkt auch mit "_" löschen. Nur : Daten ... schreibt nach der letzten beschriebene Adresse weiter, und erlaubt so längere Eingaben. Dies geschieht ohne die StartAdresse und den alten Inhalt ausgeben. Damit kann man beliebige Daten und Programme eingeben und modifizieren. Wie dieses kleine Demoprogrämmchen:

Benutzer:   300.30F
Rechner:    0300: 00 00 00 00 00 00 00 00
Rechner:    0308: 00 00 00 00 00 00 00 00
Benutzer:   300: A9 8D 20 EF FF A9 C1 20 EF FF 4C 1F FF
Rechner:    0300: 00
Benutzer:   300.30F
Rechner:    0300: A9 8D 20 EF FF A9 C1 20
Rechner:    0308: EF FF 4C 1F FF 00 00 00
    

(Apple II kann auch einen Ausschnitt vom Speicher an einen anderen Ort umkopieren. Damit kann er zudem Speicher mit Kopien eines einmal gegebenen Musters füllen, bzw mit als Muster nur ein Byte von 0 auch Speicher löschen. Ebenso kann er 2 Ausschnitte vom Speicher vergleichen, mit nur die Differenzen ausgebend.)

Kommando Starten

Als Kommando Adresse gefolgt von R (Run) eingeben gibt wieder zuerst den Inhalt der Adresse aus, und startet dann ein Program ab dieser Adresse. Nur ein R nimmt wieder die letzte angezeigte Adresse. Falls man gerade von einer StartAdresse: Daten Eingabeserie kommt, nimmt es dessen erste einzige ausgegebene (und überschriebene) Adresse, und nicht etwa die letzte überschriebene! Selbst nach mehrern : Daten... gilt dies, nur die letzte ausgegebene Adresse zählt. Das Adresse und R Kommando gibt ebenfalls erste Adresse und Inhalt aus, R alleine aber nicht. R kann wiederholt die selbige Adresse starten. Man kann auch fast beliebig Adresse, StartAdresse.EndAdresse, StartAdresse: Daten ... und AdresseR in einer Kommandozeile mischen, bis zu den maximal 128 Zeichen. Genauer darf nach einem ":" keine Adresse mehr in der Zeile erscheinen, weil alle Zahlen ab dann Daten sind.

Die Cursorposition bleibt nach dem angezeigten Inhalt stehen, falls nach AdresseR oder StartAdresse: Daten ... R vorhanden. Sonst am Anfang einer leeren Zeile, falls nur R benutzt. Programme sollten daher normalerweise mit Return ausgeben anfangen. Nach jedem Kommando, und so auch R und dann Programende gibt der Monitor ein Return aus, also kann man sich wiederum dort eines ausgeben einsparen. Das AdresseR und R kann dann bei obigem Demoprogrämmchen so aussehen:

Benutzer:   300R
Rechner:    0300: A9
Programm:   A
Benutzer:   R
Rechner:    ... nichts hier ausgegeben, weil keine Adresse eingegeben!
Programm:   A
    

(Apple II kann neben Programm stur laufenlassen auch zwecks debuggen es schrittweite ausführen, mit nach jeden Schritt alle Register anzeigen. Oder stur laufenlassen mit vorzu die Register anzeigen. Ebenso kann es nach normal laufenlassen explizit die Register anzeigen. Zudem kann es ihren Inhalt modifizieren und Programm fortsetzen. Apple II Plus sein Autostart Monitor wurde aber zu gross und hat daher das schrittweite ausführen fallengelassen.)

Kassettenband

Wer Daten/Programme abspeichern und laden will, braucht das Kassettenband Interface. Dieser benutzt das Signal "R" am Slotstecker, welches mit Cxxx verbunden sein muss. Dessen eigene Software wird vom Monitor aus mit C100R gestartet. Es bietet dann eine eigene Kommandozeile nach gleichem Muster an: StartAdresse.EndAdresse, gefolgt von W (Write) schreibt, gefolgt von R (Read) liest. Nach jedem Kommando gibt es automatisch "Abbruch" zum Monitor. Weiteres Kommando braucht daher zuerst wieder C100R, aber man kann mehrere Kommandos in einer Zeile eingeben, aber nur alle schreibend oder alle lesend, anderes ist wegen Bandlaufwerk Verhalten sinnlos.

(Apple II kann direkt schreiben mit W und lesen mit R, ohne zuerst ein C100R, weil Kassettenband Interface und Software bereits eingebaut sind. Daher braucht er für starten auch G (Go) statt R (Run).)

Basic kommt auf Kassette daher, braucht die zweite Bank DRAM, auf E gelötet, und wird mit C100R und dann E000.EFFFR geladen, was 30 Sekunden dauert, plus Band zurückspulen. Apple empfiehlt, dieses nach dem ersten laden auf ein zweites Band zu backupen, mit C100R und E000.EFFFW. Basic starten ist vom Monitor aus mit E000R.

(Apple II hat Basic in ROM, muss es daher nicht laden. Es kann direkt mit Ctrl-B (Basic) gestartet werden. Es wird sogar mit dem Apple II Plus Autostart Monitor nach Reset automatisch gestartet. Weshalb man dort mit Befehl CALL an Adresse $FF69 den Monitor starten muss, der wiederum mit Ctrl-C (Continue) verlassen werden kann.)

Beliebige Bytes

Wer sich nun fragt, was all die als Hexzahlen ausgegebenen und eingetippten Daten eigentlich bedeuten: Die minimale Antwort ist: beliebiges! Die genaue Antwort: Sind sie einfach eine Darstellung der 8 Bits der Bytes im Speicher. Dies in hexadezimaler Notation. Dabei werden die 8 Bits zu 2 mal 4er Gruppen zusammengefasst um Tipparbeit zu sparen. Wobei man nur noch auswendig lernen muss, dass es pro 4bit stets 2^4 = 16 Kombinationen gibt, bei welchen folgende kleine 4x4 Tabelle gilt:

0=0000  1=0001  2=0010  3=0011          00xx sind also 0123
4=0100  5=0101  6=0110  7=0111          01xx sind also 4567
8=1000  9=1001  A=1010  B=1011          10xx sind also 89AB
C=1100  D=1101  E=1110  F=1111          11xx sind also CDEF

  xx00    xx01    xx10    xx11
  sind    sind    sind    sind
  also    also    also    also          02468 und ACE sind   gerade Ziffern
  048C    159D    26AE    37BF          13579 und BDF sind ungerade Ziffern

obige Adresse FF00, Ausgabe Daten D8, sind D=1101 und 8=1000, also 1101'1000
obige Adresse 0300, Eingabe Daten A9, sind A=1010 und 9=1001, also 1010'1001
    

Was diese Bitmuster wiederum bedeuten, hängt davon ab, was für Daten man gerade am eingeben, bzw Programm man am codieren ist. Für Daten will man entweder direkt binäre Zahlenwerte eingeben, oder Textcodes.

Für Text braucht man eine Tabelle der ASCII Codes 00..7F (weil 7bit Umfang, damit Zahlenwerte Dezimal 0..127 bzw Hex 00..7F). Manche Rechner wie der Apple 1 (und auch Apple II) verlangen nach ASCII mit Bit7=1, weshalb statt 00..7F dann 80..FF verwendet werden müssen. Ebenso werden wegen der 6bit Terminal Logik die Kleinbuchstaben 60..7F (bzw eben E0..FF) zu Duplikaten der Grossbuchstaben 40..5F (bzw eben C0..DF). Somit entsteht:

      ASCII     Apple     Zeichen
    Dez   Hex  +Bit7=1  +x0 1 2 3 4 5 6 7 8 9 A B C D E F 

  0..15    0x    8x       --- nur 0D = Zeilenumbruch, rest unbenutzt ---
 15..31    1x    9x       ---          alle unbenutzt                ---
 32..47    2x    Ax         ! " # $ % & ' ( ) * + , - . /     (2513)
 48..63    3x    Bx       0 1 2 3 4 5 6 7 8 9 : ; < = > ?     (2513)
 64..79    4x    Cx       @ A B C D E F G H I J K L M N O     (2513)
 80..95    5x    Dx       P Q R S T U V W X Y Z [ \ ] ^ _     (2513)
 96..111   6x    Ex       @ A B C D E F G H I J K L M N O     (Duplikate)
112..127   7x    Fx       P Q R S T U V W X Y Z [ \ ] ^ _     (Duplikate)
    

Der Text "APPLE 1" wird damit zu:

A in Zeile Cx + Spalte x1 = C1
P in Zeile Dx + Spalte x0 = D0
L in Zeile Cx + Spalte xC = CC
E in Zeile Cx + Spalte x5 = C5
Leer in Z  Ax + Spalte x0 = A0
1 in Zeile Bx + Spalte x1 = B1
    

Also insgesammt C1 D0 D0 CC C5 A0 B1. Dies an 0400 eingeben wird zu:

Benutzer:   400.407
Rechner:    0400: 00 00 00 00 00 00 00 00
Benutzer:   400: C1 D0 D0 CC C5 A0 B1
Rechner:    0400: 00
Benutzer:   400.407
Rechner:    0300: C1 D0 D0 CC C5 A0 B1 00
    

Für Programmcode gibt der Prozessorhersteller in seinem jeweiligen Handbuch die Beschreibung der Befehle und ihrer Codes. Dies zumeist auch mit in einem Anhang zwei Listen der Codes. Die erste nach Befehlen geordnet, um diese in Hexzahlen zu übersetzen (zumeist einfach alphabetisch sortiert, bessere sind nach logischen Kriterien tabelliert). Die zweite nach Hexzahlen geordnet, um diese in Befehle zu übersetzen (oft einfache Liste, bessere wieder als Tabelle angeordnet). Im Apple 1 wird eine 6502 verwendet. Siehe zu dieser gleich anschliessend ihre Beschreibung.

Viele Benutzer haben darauf aufbauend ihre eigenen verbesserten Tabellen geschrieben, insbesondere auch Tabellen gemacht wo der Hersteller nur Listen lieferte. Siehe weiter unten am Ende der Prozessor Beschreibung für meine Zusammenfassung des 650s Prozessors, inklusive meiner Tabellen. Das nachschauen war aber auch so immer noch langsam und mühsam, weshalb man die häufigen Befehle bald auswendig lernte.

(Apple II hat als eine seiner Erweiterungen, dass diese Listen zum übersetzen mit eingebaut sind, der Rechner dies für einem gleich selber machen kann. Was dort als LIST (Ausgabe) bzw Mini-Assembler (Eingabe) bekannt ist. Wobei strikte nur LIST ein eingebauter Befehl ist, aber der Mini-Assembler ein externes Program welches mit F666G gestartet wird. (Wobei letzteres nur auf Apple II mit Integer Basic der Fall ist, deren 6k ROM damit aufgefüllt wurde. Auf Apple II Plus mit Applesoft II Basic wurde dieses zu gross und hat den Mini-Assembler wieder verdrängt.)

6502 Prozessor

Obiges ist den Apple 1 Monitor benutzen. Aber was sind nun die Programm Bytes? Diese sind 6502 Maschinencode, der nun beschrieben wird. Die Geschichte der 6502 ist ebenfalls weitherum bekannt. Ich werde sie daher hier nur kurz zusammenfassen, plus noch ein paar unbekanntere Sachen addieren. Neben Intel ihre 8080, anfangs 1974, als revidierte 8008, kam Motorola mit ihrer 6800 heraus, ende 1974, und wurde der zweite grosse Hersteller. Mehrere andere Hersteller welche auch 1974..76 herauskamen, hatten in ihren Prozessoren zuviele Designfehler und/oder Marketingfehler und setzten sich nicht durch.

Beide diese waren recht grosse und teure Chips (eine 8080 kostete anfangs $360, 6800 anfangs $300). Einige der Motorola Leute meinten es sollte kleiner und billiger gehen, wurden aber vom Management geblockt. Sie verliessen die Firma und landeten bei MOS Technology. Entgegen weit verbreiteter Ansicht haben sie MOS nicht selber gegründet, da diese bereits seit 1969 existierte! (Motorola selber ging mit der 6809 zu einem grösseren Chip. Intel revidierte die 8080 weiter zur unwesentlich grösseren 8085, und deren Leute welche grösser haben wollten und deswegen geblockt wurden gründeten Zilog und brachten die erweiterte Z80 heraus. Damit haben wir bereits alle bedeutenden 8bit Prozessoren durch.)

Die 8080 brauchte 4500 Transsitoren in der kompakteren 12V Enhancement Mode NMOS Technologie. Die nur leicht erweiterte 8085 brauchte 6500 wegen der aufwendigeren Depletion Mode NMOS 5V Technologie. Die 6800 hatte nur 4100 in 12V Technologie mit eingebautem 5zu12V Spannnungswandler, dann revidiert 5000 in 5V, war also schon etwas kleiner. Dagegen brauchte die 6502 nur noch 3510 trotz von Anfang an 5V Technologie! (Die PIA 6820 ist auch in 12V mit 5zu12V Wandler, 6821 revidiert in 5V, aber leicht inkompatibles Timing und daher andere Nummer. Die 6520 ist ein Clone mit von Anfang an 5V.)

Ein derart kleinerer Chip ist nicht nur billiger, sondern hat auch weniger Defektexemplare zum wegwerfen, was ihn nochmals billiger macht. Folglich wurde die 6502 mit $25 eingeführt, als 8080 und 6800 noch $150 bzw $175 kosteten. Diese wurden beide über Nacht auf $75 reduziert. (Alle Zahlen sind wegen Inflation für heute 2018 mit etwa *4 zu korrigieren. Das entspricht also nur $100 statt andere $600 bzw $700, welche dann zu $300 wurden. Bzw eine 8080 anfangs sogar $1440, und 6800 anfangs $1200!)

Alle diese Prozessoren sind 8bit mit 2*8=16bit Adressen, als 256 Pages (0..255) zu je 256 Bytes (0..255) organisiert. Alle verwenden, wie fast alle anderen 8bit auch, die 1-Adress Logik, bei der eine Rechnung der Sorte V1=V2+V3 in drei Schritte Temp=V2; Temp=Temp+V3; V1=Temp zerlegt werden muss. Das Temp ist ein 1Byte Register (eine Speicherstelle im Prozessor drin), und wird Akkumulator oder kurz A genannt. Die 8080 hat im Prozessor zudem 6 Bytes für lokale Variablen. 6800 und 6502 sparen sich diese ein. Die 6800 kompensiert das mit 2 Akkus A und B haben, die 6502 lässt selbst dies weg, hat nur ein Akku A. Die 8080 hat dabei 8 Rechenoperationen, die 6800 sogar 9, die 6502 aber nur 7 davon. Man sieht hier deutlich woher das kleiner und billiger kommt!

Die 8080 muss bei mehr als 6 Variablen explizit vom Speicher holen (verbesserte Methoden dazu sind der Hauptunterschied von 8008 zu 8080), ausser Konstanten welche stets implizit sind (bei allen hier). 6800 und 6502 dagegen holen implizit bei jeder Operation, inklusive Konstanten (ausser die 6800 nimmt Daten von B zu A). Dies macht beide einfacher zu programmieren, aber auch langsamer wegen weit mehr Speicherzugriffen, mit neben Befehl auch noch Adresse und Daten transferieren. Was sie aber mit bei gleicher Technologiestufe etwa doppelte Menge Speicherzugriffe teilweise kompensieren können (sofern der Speicher mithält).

Dazu hat die 6800 noch ein 2 Byte Hilfsregister (Indexregister genannt, es ist aber eigentlich nur ein Basisregister), die 6502 dagegen 2 separate mit je 1 Byte (und erst noch echte Indexregister). Die 6800 kennt dazu neben Konstante auch 3 Methoden den Speicherort zu definieren (2 ohne und 1 mit Hilfregister), welche als Adressmodi bekannt sind. Die 6502 kann erstaunlicherweise sogar ganze 7 Adressmodi (2 ohne und 5 mit den beiden Hilfsregistern), was ihre Hauptstärke ist, wodurch sie trotz einfacher sein mit der 6800 etwa gleichziehen kann, und an manchen Stellen wegen effektiver ablaufen (weniger Totzyklen mit abwarten von Operationen) sogar schneller sein kann. (Die 8080 nimmt Paare ihrer 6 Variablen als Hilfsregister, kann neben Konstante nur 2 Methoden den Speicherort zu definieren (1 ohne und 1 mit Hilfregister).)

Befehlssatz

Da dies ein Apple 1 Text ist, und nicht ein 6502 Text, werden hier nur die wichtigsten Befehle und ihre Basiscodes und Adressmodi davon eingeführt, welche oft verwendet werden, um den nachfolgenden Programmcode vom Monitor nachvollziehen zu können:

Alle Befehle sind stets 1 Byte, mit danach allenfalls 1 Byte an Konstante bzw 1 oder 2 Bytes an Adressen (je nach Adressmodus). Alle 16bit Adressen werden in 2 Bytes zerteilt wegen 8bit Prozessor. Daher besteht der 64k Speicher auch aus 256 Pages (Nummern 0..255) zu je 256 Bytes (0..255), wegen 2*8bit. Zudem werden diese beiden Bytes reversiert gespeichert, zuerst die untere/niedere/Bytenummer Hälfte und danach die obere/höhere/Pagenummer, für falls beim Gebrauch der Index Hilfsregister und der dabei benutzter Addition Übertragsrechnungen anfallen. (Die 8080 macht das auch reversiert. Die 6800 aber nicht, muss daher bei erstem Byte lesen pausieren, zweites verarbeitem, dann einen Totzyklus erstes nacharbeiten.)

Befehl LDA: Ist Lade (= LoaD) zu Akkumulator vom Speicher, macht A = Daten-Konstante oder Daten-Variable ohne etwas zu rechnen. Gefolgt von #Daten bedeutet dieser Befehl eine 1 Byte Konstante holen, genauer vom Speicher das Byte gleich nach dem Befehl, was daher Immediate Adressierung heisst. Bei reinem # ist die Konstante in Dezimal, bei #$ die Konstante in Hex gegeben, intern sind beide ohnehin in Binär. Bei nur $ ohne # ist von Speicher holen, mit expliziter Adresse in Hex. Ohne beides ist mit expliziter Adresse in Dezimal. Falls 4-stellig eine volle 2 Byte 16bit Adresse als Modus, ist sie wie oben reversiert. Oder falls 2-stellig eine kompaktere und schnellere 1 Byte 8bit Adresse als Modus, welche mit 00 davor erweitert wird. Daher sind auch die 0000..00FF Zeropage als RAM so wichtig. (All dies gilt ebenso bei der 6800, nicht aber bei 8080.) Die weiteren fünf Adressmodi mit Hilfsregister sind seltener, werden hier ignoriert, bzw wo benutzt vor Ort beschrieben. (LDA hat als Basiscode A1, mit Adressmodi Konstante/Immediate +08 = A9, 16bit Adresse +0C = AD, 8bit Adresse +04 = A5.)

STA: Ist das selbige umgekehrt, Speichere (= STore) von Akkumulator zum Speicher, macht Daten-Variable = A. Geht aber nicht mit Konstante, weil in eine Konstante speichern sinnlos ist und mit einer Hardwareoptimierung kollidoert. Ansonsten hat es selbige Adressmodi. (STA hat als Basiscode 81, Rest der Adressmodi wie bei LDA, nur ohne Konstante.)

LDX,STX,LDY,STY: Sind selbige beides mit den zwei Hilfsregistern, welche X und Y heissen. LDX und LDY sind X = Daten bzw Y = Daten, inklusive Adressmodus Konstante, STX und STY sind Variable = X bzw Variable = Y, ohne Konstante. (Basiscodes sind LDX A2, STX 82, LDY A0, STY 80. Man sieht alle LDx haben Ax, alle STx haben 8x, ebenso alle xxA haben x1, alle xxX haben x2, alle xxY haben x0 (und mit x3 gibt es gar keine Opcodes). Solche Systematiken helfen auswendig lernen. Adressmodi sind selbige +0C und +04 wie gehabt, aber bei Konstante/Immediate ist hier +00 statt +08. Das ist komplett irregulär, und nervig zum lernen. Noch schlimmer, das Gebastel um dies im Prozessor zu machen beinhaltet erst noch einen Prozessorbug, der bei Codes 02 22 42 62 sicher, und nach manchen Aussagen bei allen x2 ausser A2, den Prozessor abstürzt! Alles ein Fall von überhastetem Design zurechtflicken statt richtig ausarbeiten.)

ADC,SBC,AND,EOR,ORA: Sind rechnen mit Akkumulator und Daten vom Speicher, machen A = A <Operation> mit Daten-Konstante oder Daten-Variable. Daten wie gehabt, inklusive Konstante. Operation ist: ADC (= ADd with Carry) macht Addition, SBC (= SuBtract with Carry) macht Subtraktion, AND,EOR,ORA machen die logischen Operationen AND XOR und OR. ADC und SBC erzeugen einen Übertrag, und benutzen auch stets den Übertrag von der letzten Rechnung. Es gibt hier keine ADD und SUB ohne Übertrag von letztem (wie sie bei 6800 und 8080 vorhanden sind). Die Implikationen davon werden in der Sortware anschauen vor Ort beschrieben. (Basiscodes sind ADC 61, SBC E1, AND 21, EOR 41, ORA 01, wieder alle mit x1 weil auf den Akkumulator A wirkend. Ebenso sind Adressmodi wie bei LDA, inklusive Konstante/Immediate.)

CMP,CPX,CPY,BIT: Sind testen A oder X oder Y durch Vergleich mit Daten-Konstante oder Daten-Variable. Operation ist bei CMP,CPX,CPY vergleich (= CoMPare) mit testweiser Subtraktion. Also ohne das Resultat in A oder X oder Y abzulegen, nur der bei allen LDx oder Rechnungen gesetzte Teststatus wird hier doch gesetzt. Diesmal ohne Übertrag von der letzten Rechnung, aber erzeugt einen Übertrag. Daten wie bei LDA bzw LDX bzw LDY gehabt, inklusive Konstante. Operation is bei BIT (BInary Test) nur mit A als testweises AND ohne abspeichern, und dazu noch Operand alleine Bit7 und Bit6. Ohne Konstante. (Basiscodes ist CMP C1, wieder x1 weil Akkumulator. Adressmodi sind wie LDA. Dieses bricht aber die Namensregel, sollte eigentlich CPA heissen. Aber weiter sind CPX E0 und CPY C0. Wobei das CPX sowohl die CPx sind Cx wie auch die xxX sind x2 Coderegeln bricht! Adressmodi wie LDX und LDY. Und BIT ist 20. Dieses kann gar keine Konstante, wegen dem Regelkonflikt in der Prozessorverdrahtung, vom Konstanten/Immediate Adressmodus ihrer +00 bei xxX und xxY aber +08 bei xxA. Was nervt wenn man diesen Modus brauchen würde.)

INX,DEX,INY,DEY: Sind INcrement (= +1) und DEcrement (= -1), als Rechnen mit den Hilfsregistern, machen X = X+1, X = X-1, Y = Y+1, Y = Y-1, zumeist um die nächste oder vorherige Adresse zu erzeugen. (Codes sind INX E8, DEX C8, INY C0, DEY 80, komplett irregulär. Es hat bei diesen logischerweise keine Basiscodes + Adressmodi, da auch kein Speicher benutzt wird oder adressiert werden muss.)

BPL,BMI,BVC,BVS,BCC,BCS,BNE,BEQ: Sind verzweige (= Branch) von der linearen Fortsetzung des Programs, falls eine von 4*2=8 Bedingungen erfüllt sind oder nicht. Diese testen falls das Ergebnis der letzten LDx oder Rechnung oder Vergleich resultierte in: BPL (= Branch PLus) falls positiv, BMI (= MInus) falls negativ, BVC wird wo einmal benutzt vor Ort beschrieben, BVS wird hier nicht benutzt, BCC falls kleiner, BCS falls grösser-oder-gleich, BNE (= Not Equal) falls ungleich 0, BEQ (= EQual) falls gleich 0. Bei nicht erfüllt wird linear weiter gefahren, aber bei erfüllt wird zur Adresse nach dem ganzen Bxx Befehl eine 1 Byte 8bit Adressdifferenz vom zweiten Byte addiert, was -128..+127 Adressen an Sprungdistanz erlaubt. (Codes sind BPL 10, BMI 30, BVC 50, BVS 70, BCC 90, BCS B0, BNE D0, BEQ F0, also alles "ungerade" mit x0, wieder einfach zu merken.)

JMP: Ist gehe (= JuMP) ohne Bedingung zu einem anderen Ort im Programm, und führe ab dort linear aus. Hier stete als 2 Byte 16bit Adresse gegeben, keine 1Byte 8bit Adressdifferenz. (Code ist 4C.)

JSR,RTS: Ist JSR gehe (= Jump) ohne Bedingung temporär (= SubRoutine) zu einem anderen Ort im Programm, und führe ab dort linear aus. Wieder als 2 Byte 16bit Adresse gegeben. Dies aber nur bis ein RTS (= ReTurn from Subroutine) hierhin zurückschickt. Zu merken wo "hierhin zurück" war und wieder sein soll, braucht es die 0100..00FF Stackpage als RAM, sonst versagt der JSR dies abzuspeichern und das RTS geht einfach irgendwo hin weitermachen. Die Limite auf eine Page ist die Folge eines weiteren nur 1 Byte grossen Hilfsregisters. (Diese Limite gilt nicht bei der 6800 oder 8080, welche beide dazu 2 Byte Hilfsregister haben.) (Codes sind JSR 20, RTS 60. Der JSR 20 kollidiert mit BIT 20 wenn mit +00 Konstante, und ist wohl der Grund warum diese Kombination nicht existiert. Warum JSR nicht den weit passenderen (und unbenutzten!) 0C hat liegt an einer internen Vereinfachung, welche ihn schneller als 6800 macht (nur 6 statt 9 Takte), zum Preis von RTS langsamer (6 statt 5), zusammen dann 12 statt 14, also 14.2% schneller.)

Alle anderen Befehle sind hier genug selten, oder gar unbenutzt, dass sie falls vorhanden vorzu beschrieben werden.

Mit den Codes wissen, wird obiges Demoprogrämmchen von Hexzahlen: A9 8D 20 EF FF A9 C1 20 EF FF 4C 1F FF, zu aussprechbareren (und so auch merkbareren) Befehlen: LDA #$8D; JSR $FFEF; LDA #$C1; JSR $FFEF; JMP $FF1F. Mit den Befehlen kennen, wird das weiter zu: LDA #$xx die Konstante xx in A laden, sowie JSR $Fxxx ein Unterprogramm bei $Fxxx aufrufen, und JMP $Fxxx wegspringen zu $Fxxx. Mit zudem der Apple 1 Hardware kennen, weiss man dass alle Adressen $Fxxx im PROM liegen, womit man sieht, dass dies alles Systemaufrufe sind, mit den LDA Konstanten als Parameter, Zeichen 8D=Zeilenumbruch und C1=A ausgeben und dann abbrechen.

(Wer mehr Details zur 6502 wissen will, kann das originale Handbuch anschauen.)

(Oder kompakter meine Zusammenfassung davon, mitsammt meinen beiden Tabellen am Schluss. Zu unterst ist Zahlen zu Befehle zurück übersetzen (um Code zu lesen). Zur Repetition der Codes ist in obigem Demoprogrämmchen das erste Byte A9. Zu Befehl übersetzen mit zuerst oben quer sehen dass es 00 bis 07 hat, daher x9 zu 08+01 zerlegt werden muss, wobei +01 die zweite Spalte ist, dann links bis zu A0+08=A8 herunter und dort bis zur zweiten Spalte rein, und man landet bei dem LDA #. Gleich darüber ist die nach logischen Kriterien sortierte Befehle zu Zahlen. Für z.B. obiges LDA # zu Hexzahl übersetzen (um Code einzugeben), zuerst zur zweiten Gruppe gerade Zahlen 01..E1 (Akkumulator Arithmetik) gehen, LDA ist die A1 Zeile und LDA # ist dritte Spalte (LDA z ist 1 Byte 8bit Adressen und LDA hl ist 2 Byte 16bit, alles mit ,X oder ,Y darin benutzen diese Hilfsregister), dann hinauf zu +08 für Adressmodus, zu A1 addiert gibt dies A9. Der effiziente Benutzer hatte diese beiden Tabellen auf den zwei Seiten eines A4 Zettels notiert. Solche Tabellen benutzen ist somit etwas aufwendig aber nicht schwierig, und daher auch durch häufiges benutzen schnell auswendig lernbar, aber ebenso in LIST und im Mini-Assembler einfach automatisierbar.)

(Man sieht in dieser oberen zweiten Tabelle bei A0/A1/A2 LDx und 80/81/82 STx auch obige x0=xxY x1=xxA x2=xxX und x3=nichts Logik, an den vier Blöcken, mit dem letzten stets leer. Man sieht aber auch bei C0/E0/C1 den Bruch mit CPX in der E0 statt C2 Zeile. Man sieht ebenso wie die +04 und +0C Spalten durchgehend z bzw hl Adressmodi sind (und die +18 und +1C Spalten z,X bzw hl,X). Man sieht aber auch den Bruch mit +08 als # nur bei x1, weil x0 und x2 auf +00 ausbrechen, diese mit ?? dahinter markiert. Ebenso sieht man wie bei 20 +00 statt BIT # sein, vom JSR weggenommen wird, trotz dass dieser wegen hl Modus eigentlich in die +0C Spalte gehören würde. Ebenso sieht man, dass BIT keine z,X oder hl,X Adressmodi hat (bzw am klein getippten bit z,X und hl,X dass diese erst in der 65C02 addiert wurden). Sowie dass JMP und JMP() sogar alle Modi ausser hl fehlen (aber 65C02 addiert hl,X für JMP()). Sowie einiges mehr an Irregulärem, bei STX/LDX die z,X und hl,X sinnvoll zu z,Y bzw hl,Y korrigiert, diese mit ! dahinter markiert. Sowie manches weniger sinnvoll angeordnetes, mit !! dahinter markiert. Aber auch CPY und CPX lassen die z,X und hl,X weg, wohl weil CPX falsch liegt, so die z,Y bzw hl,Y Korrektur von STX/LDX nicht greifen kann. Eigentlich sollten alle Codes mit --- ? eigentlich die dort kreuzenden Befehl+Adressmodus Kombinationen beinhalten, die aber schlicht fehlen. Generell ist dies ein ziemlich unsorgfältig designter Befehlssatz, im Vergleich zu 6800 und 8080, vermutlich weil überhastet gemacht, weil es von der 6800 Einführung via Firma erlassen bis zur 6502 Einführung nur ein Jahr dauerte.)

Systemsoftware Programmcode

Das ist der 6502 Prozessor im Apple 1. Nach dieser kurzen Einführung in dessen Programmierung können wir nun den Programmcode vom Monitor lesen und analysieren. Dieser wurde im Apple 1 Handbuch abgedruckt als Listing, Seiten 5 und 6. Ich habe eine selbst erstellte etwas ausgebaut kommentierte Form davon als Listfile in Englisch auf meiner Website, welche dann übersetzt die Basis vom Code weiter unten ergibt. (Zudem hab ich den Monitor als Vergleichsexperiment auf die 6800 übersetzt, sowie auf deren Nachfolger 6809. Beide etwa gleich kompakt.)

Ich gehe hier aber die Software durch, in der Reihenfolge wo Sachen benutzt werden, oder vor ihrer Nutzung als Grundlage aufbauen sinnvoll ist. Wer es linear haben will, kann das Handbuch anschauen gehen, oder obige Datei auf der Website, diese aber beide in Englisch. Das ganze hier nennt man einen Code Walkthrough. Danach werdet ihr wissen wie man obige recht ordentliche Kommandozeile als Interface in nur 256 Bytes implementieren kann. Unter anderem mit an jedem Ort jedes vermeidbare Byte einsparen!

Aufstarten

Als erstes starten wir den Prozessor:

Adress Hexbytes   Programcode    Kommentar

FFF8   00 00      .DB $00 $00    ungenutzter leerer Platz(!), zwei Bytes
FFFA   00 0F      .DW $0F00      6502 nach NMI, in Apple 1 unbenutzt
FFFC   00 FF      .DW RESET      6502 nach Einschalten/Reset, starte bei $FF00
FFFE   00 00      .DW $0000      6502 nach IRQ, in Apple 1 unbenutzt
    

Alle Programm Ausschnitte ab hier haben obiges Tabellenformat, mit Adress(e) + Hexbytes + Programcode + Kommentar. Gefolgt von einem Abschnitt wie diesem, der weiter kommentiert was und warum es geschieht. Die Zeilenkommentare reichen für Assembler Programmierer. Aber da die meisten Leute keinen oder wenig Assembler können, sind zudem die Abschnittkommentare vorhanden.

Die letzten 3 Zeilen definieren je eine 2-Byte 16bit Adresse (= ein Wort, das .DW bedeutet "Define Word"), welche von der 6502 verlangt werden. Die in $FFFC und $FFFD liegende Adresse ist zwingend, und muss auf den Anfang vom System seinem Programcode zeigen, sonst stürzt die 6502 gleich nach Einschalten (bzw Reset) ab! Hier steht als Anfang RESET = $FF00, (und weil zuerst das untere/niedere und danach obere/höhere Byte als Hexbytes 00 FF), weil das erste Byte des PROMs das den Namen RESET hat (was der Assembler dann zu FF00 ünersetzt). Die Adressen in $FFFA und $FFFB sowie $FFFE und $FFFF braucht es falls man 2 Hardware Features NMI bzw IRQ nutzt, welche aber im Apple 1 unbenutzt sind. Ich gehe daher nicht weiter auf diese ein.

(Strikte kann man mit Lötpunkten neben der PIA 6820 die Terminal Logik von IRQB/DSP an NMI und die Tastatur von IRQA/KYBD an IRQ legen. Aber die Monitor Software kann ohne Programm bei $0F00 bzw $0000 damit nicht umgehen, stürzt beim ersten Zeichen ausgeben bzw einkommen ab, bevor der Benutzer überhaupt Code dafür eingeben kann! Womit dieses Feature praktisch unbenutzbar ist, ausser man ersetzt die PROMs.)

Zudem hat es als erste Zeile 2 Bytes an leerem Platz füllen (das .DB bedeutet "Define Byte"), weil der Monitor eigentlich nur 256-8 = 248 Bytes braucht! (Diese sind als 22 Stück 1 Byte und 80 Stück 2 Byte und 22 Stück 3 Byte Befehle, weil alle bei grösseren Programmen eher häufiger benutzten 3 Byte hier selten benutzt sind.)

Treiber

Jedes System braucht Treiber. Der Monitor hat davon genau zwei, beide für den 6820. Die Tastatur/Eingabe wird nur an einem Ort benutzt, und befindet sich daher direkt dort eingebaut, um je einen JSR und RTS ihre 3 bzw 1 Bytes zu sparen. Wir werden diesen später zu sehen bekommen. Die Anzeige/Ausgabe wird aber mehrmals benutzt, und ist daher als Unterprogramm vorhanden. Es befindet sich am Schluss, gerade vor obigem $FFF8:

ECHO:
FFEF   2C 12 D0   BIT DSP        6820 Port B, Test ob bereit Zeichen anzunehmen
FFF2   30 FB      BMI ECHO       Bit7 = 1 ist nicht bereit, warten bis 0 wird
FFF4   8D 12 D0   STA DSP        6820 Port B, schreibe Zeichen zu Anzeige
FFF7   60         RTS
    

Dieses Unterprogram heisst ECHO, weil benutzt um die Eingaben zwecks Bestätigung wieder auszugeben, und ist ab Adresse $FFEF. Es testet die 6820 auf Status ob bereit. Dies macht er mit dem BIT Test (Basiscode 20), mit einer 16bit Addresse (Adressmodus +0C = 2C), von Adresse DSP der 6820 (= $D012, reversiert als 12 und D0 gespeichert). Dies testet Bit7, also den Pin PB7, und somit den dorthin verbundenen Pin CB2. Dann wartet er, wegen dem BMI (30), den BIT Test solange wiederholend wie die Ausgabe mit Minus=1=besetzt ist, bis es zu Plus=0=bereit wird, mit dem FB als Adressdifferenz -5 um 5 Bytes zurück zu springen, von $FFF4 (nach den BMI und -5 lesen) zu $FFEF (für nächstes BIT). Das STA (81+0C = 8D) auf Adresse DSP der 6820 (= $D012, revers als 12 D0) sind wie gehabt. Dies gibt Pins PB0..6 aus und setzt CB2, worauf die Anzeige dieses Zeichen verdaut. Der RTS (60) ist geradeaus. Damit Treiber erledigt in 9 Bytes. (Das sollte als Repetition der Anwendung der Codes ausreichen, ab jetzt werden nur noch die Befehle angeschaut.)

Erstaunlicherweise testet dies Bit7 (Datenbit7) vom 6820 Ausgabedaten Register an $D012 und nicht etwa Bit7 (IRQB1) vom Status Register an $D013. Wozu extra eine Leitung vom externen Statuspin CB2 an den Datenpin PB7 (und somit Datenbit7) gezogen wurde! Was als Seiteneffekt die Ausgabe auf 7bit reduziert, während Eingabe und Prozessor und Speicher alle eigentlich 8bit sind. Der Grund dafür ist mir unbekannt. Ich spekuliere aber dass dies 3 Bytes spart, statt einmal nach dem Reset von $D013 lesen zu müssen um Status IRQB1 zu löschen, weil Terminal nach Reset bereit werdend würde dann bereits gesetzt haben.

(Beim Apple II ohne die ganze Terminal Logik, muss diese in Software nachgebildet werden. Diese braucht etwa 200 Bytes, statt obigen 9. Damit ist sie alleine für (2048-256)/200 = etwa 1/9 der ROM Platzzunahme dort zuständig! Dieses beinhaltet aber auch den positionierbaren Cursor und die scrollbare Ausschnitte Funktionen.)

Hilfsfunktionen

Neben Treiber brauchen Systeme weitere Hilfsfunktionen, als Systemlibraries bekannt. Der Monitor hat auch hiervon zwei, beide als Unterprogramme, die zweite den ersten wiederum selber zweimal benutzend. Der erste gibt eine Zahl 0..15 als Hexziffer aus:

PRHEX:
FFE5   29 0F      AND #$0F       lösche jegliche oberen Bits von Ziffer
FFE7   09 B0      ORA #"0"+$80   nicht-ADC "Addition" von "0", mit Bit7 = 1
FFE9   C9 BA      CMP #"9"+$80+1 ist Result <= "9", und so eine Dezimalziffer?
FFEB   90 02      BCC ECHO         ja, gebe diese Ziffer aus ohne Korrektur
FFED   69 06      ADC #"A"-"9"-1 schiebe "A"-"9" = 7, aber ADC Carry=1 also 6
FFEF   !!! durchfallen in ECHO   statt JSR+RTS oder JMP
    

Dieses Unterprogram heisst PRHEX, und ist ab Adresse $FFE5. Das AND ist nur eine Vorsichtsmassnahme, falls die Zahl in A grösser als 15 ist (= maximale Hexziffer) wird sie abgeschnitten (vordere 4 Bits = 0). Danach wäre eigentlich ein ADD #$30+$80 angebracht, was die 6502 aber nicht kann! Man könnte CLC plus ADC benutzen, was aber 3 Bytes braucht. Weil hier aber nur ein $0x + $B0 gerechnet wird, was keinen Übertrag ergeben kann, macht ein ORA das in nur 2 Bytes!

Das +$80 braucht es nur, weil die Apple 1 eigenartigerweise ASCII mit gesetztem Bit7 erzeugt (der Apple II macht dies auch so). (Dies trotz dass obige 6820 Ausgabe PB7 Verdrahtung das obere Bit wieder wegwirft! Es mit der Eingabe konsistent machen ist auch fraglich, da diese 8bit ist, und nur von der Tastatur her Bit7=1 gegeben hat, weil diese so verdrahtet verlangt wird, statt auf Bit7=0.)

Die Ziffern 0..9 sind so bereits gegeben, mit dem CMP auf <= "9" (genauer auf < "9"+1) und BCC geht es in diesem Fall weiter zu ECHO. Für A..F müssen mit "A"-"9" = $41-$39 die 7 Zeichen :;<=>? und @ zwischen "9" und "A" übersprungen werden. Weil der BCC bei Carry=0 gesprungen wurde, somit beim ADC ein Carry=1 bereits gegeben ist, muss die 7 aber auf 6 herunter korrigiert werden.

Danach muss das Zeichen ausgegeben werden. Um weitere 3+1 Bytes Platz zu sparen ist kein JSR ECHO gefolgt von RTS. Es reicht hier, weil der JSR der letzte Befehl vor einem RTS ist, auch ein nur 3 Bytes JMP ECHO, was als "Tail Call Optimierung" bekannt ist. Aber auch der JMP kann eingespart werden, durch dieses Unterprogram einfach direkt vor ECHO hinstellen, mit so gleich in dieses durchfallen, was als "Fall Through Optimierung" bekannt ist. Beides sind Standardtechniken um schneller und kompakter zu sein. Erste Libraryfunktion erledigt in 10 Bytes.

Der zweite Hilfsfunktionen gibt ein ganzes Byte als zwei Hexziffern aus:

PRBYTE:
FFDC   48         PHA            speichere Byte für rechte/LSB Hexziffer
FFDD   4A         LSR A          schiebe linke/MSB Hexziffer nach rechts/LSB
FFDE   4A         LSR A            braucht 4 mal 1 bit schieben
FFDF   4A         LSR A
FFE0   4A         LSR A
FFE1   20 E5 FF   JSR PRHEX      gebe linke/MSB Hexziffer aus
FFE4   68         PLA            hole Byte für rechte/LSB Hexziffer
FFE5   !!! durchfallen in PRHEX  gebe rechte/LSB Hexziffer mit PRHEX aus
    

Dieses Unterprogram heisst PRBYTE, und ist ab Adresse $FFDC. Das PHA (und sein Gegenstück PLA am Schluss) speichern Datenbytes am (bzw holen sie vom) selbigen Ort in der Stackpage, wo auch JSR (und sein Gegenstück RTS) ihre "hierhin zurück" Adressen speichern (bzw holen). LSR ist soweit hier relevant einfach eine Division durch 2, mit 4 mal gibt es durch 16 teilen, also um eine Hexadezimalstelle von links nach rechts schieben, was die rechte Hexiffer ihre Bits durch die linke verdrängt. Ein Byte von z.B. 4A wird mit dem /16 zu 04, das A geht als Unterlauf verloren. Daher auch Zahl zuerst abspeichern. Das JSR PRHEX gibt die linke/erste Ziffer aus, bis dem ECHO sein RTS hierhin zurück schickt.

Dann holt PLA das Byte zurück, und fällt wieder direkt in PRHEX durch. Dessen AND Vorsichtsmassnahme schneidet die bereits ausgegebene linke Hexziffer ab. Obiges zurückgeholte 4A wird so zu 0A. Und ECHO gibt somit nur noch die rechte/zweite Ziffer aus, und sein RTS beendet diesmal PRBYTE und PRHEX. Strikte könnte das AND auch nur Teil von PRBYTE sein, aber es wäre am gleichen Ort, nur der Name PRHEX bei $FFE7 statt $FFE5, also wird es zu PRHEX gezählt, weil das universeller ist und nichts kostet. Zweite Libraryfunktion erledigt in weiteren 9 Bytes. Es verbleiben jetzt noch 256-8-(9+10+9=28) = 220 Bytes, von $FF00 bis $FFDB.

Konfigurieren

Jetzt wo alle Hilfsfunktionen gegeben sind, können wir das eigentliche Hauptprogramm anschauen. Es fängt an, wie in $FFFC und $FFFD angegeben, bei RESET (= $FF00), mit den 6502 und die 6820 konfigurieren:

RESET:
FF00   D8         CLD            6502 ausschalten Dezimalarithmetic Modus
FF01   58         CLI            6502 ausschalten Interruptsperre
FF02   A0 7F      LDY #$7F       6820 Pin Richtungen, Pins0..6 AUS, Pin7 EIN
FF04   8C 12 D0   STY DSP        6820 Port B Richtung, Port A bleibt #$00 EIN
FF07   A9 A7      LDA #$A7       6820 Steuerwort, CA1/CB1 mit Status verwalten
FF09   8D 11 D0   STA KBDCR      6820 Port A Konfiguration, Eingabe mit Status
FF0C   8D 13 D0   STA DSPCR      6820 Port B Konfiguration, Ausgabe mit Status
    

Die CLD und CLI sind Prozessor Konfigurationsbits, um zwei im Apple 1 unbenutzte Features abzuschalten (CLD) bzw nicht zu blockieren (CLI). Ich gehe wieder nicht weiter auf sie ein. Man beachte hier noch, dass der ganze Monitor keinen TXS Befehl (plus davor LDX #$FF) beinhaltet, womit das S Register nicht konfiguriert wird, also JSR (und RTS) sowie PHA (und PLA) zufällig irgendwo in der Stackpage benutzen werden. Das spart dafür LDX+TXS 2+1=3 Bytes.

Die LDA STA STA sind normales Konstante in Akkumulator laden, und dann weiter zur 6820 speichern. Das weil direkt nicht geht, weil die 6502 ja eine 1-Adress Logik hat, der Transfer V2=V1 daher zu Akku=V1; V2=Akku zerlegt werden muss. Die LDY STY machen genau selbiges, lassen aber das #$7F in Y, statt in A, mit es beim folgenden LDA zu verlieren, wenn hier zuerst LDA STA benutzt worden wäre. Grund für dies siehe später.

Das #$7F in DSP (= $D012) sorgt genau dafür, dass Port B Pin PB7 eine Eingabe bleibt (weil $7F = 0111'0000, also Bit7=0), womit der Status von Pin CB2 gelesen werden kann. Alle anderen Bits von Porb B sind 1 für Ausgabe. Port A bleiben alle Bits auf Defaultzustand 0=Eingabe. Das #$A7 ist eine 6820 spezifische Konfiguration. Ich gehe ebenso nicht auf diesen ein, ausser hinweisen dass selbiges in beide Konfiguration Register KBDCR (= $D011) und DSPCR (= $D013) geschrieben wird, weil bei beiden Ports wegen Handshake der Status verfolgen Modus einschaltet werden soll. Konfiguration erledigt in 15 Bytes, verbleiben 220-15=205 Bytes, von $FF0F bis $FFDB.

(Mit hier #$A6 setzen wäre der Absturz bei IRQB/DSP oder IRQA/KYBD mit Lötpunkte an NMI bzw IRQ legen verhindert worden. Weil damit wären diese inaktiv, bis der Benutzer passenden Code eingegeben hat, welcher dann auch #$A7 setzt um sie zu aktivieren. Weshalb mit so #$A7 man die NMI und IRQ nur nutzen kann, wenn man die PROMs durch andere ersetzt. Womit in diesen PROMs #$A7 nutzlos ist. Ich erachte das dementsprechend als Designbug.)

(Wer mehr Details zur PIA 6820 wissen will, kann das originale 6821 Datenblatt anschauen.)

Zeileneditor

Danach ist der Rechner fertig betriebsbereit, gebootet! Trotz nur 1.0227MHz 8bit Prozessor bootet er in unter 1ms! Ab jetzt werden nur noch endlos Eingabezeilen geholt und als Kommandos ausgewertet. Dabei kommt im Monitor zuerst die Kommandozeileneditor Tasten Auswertung und dann erst die Tasten holen, weil es so angeordnet leicht kompakter ist. Dazu muss aber der Rechnerstart durch die ganze Auswertung hindurchfallen, was es komplexer zu verstehen macht.

Ich überspringe daher zuerst mal die Auswertung in $FF0F bis $FF28, und zeige als erstes die Tasten holen, und baue dann darauf den Resten davor auf:

NEXTCHAR:
FF29   AD 11 D0   LDA KBDCR      6820 Port A Status, Test ob bereit mit Eingabe
FF2C   10 FB      BPL NEXTCHAR   Bit7 = 0 ist nicht bereit, warten bis 1 wird
FF2E   AD 10 D0   LDA KBD        6820 Port A, lese Zeichen von Tastatur
FF31   99 00 02   STA IN,Y       zu Zeilenbuffer IN, $0200..$027F, Y = Position
FF34   20 EF FF   JSR ECHO       Zeichen ausgeben, mit ECHO
FF37   C9 8D      CMP #$0D+$80   ist Taste Carriage Return, mit Bit7 = 1
FF39   D0 D4      BNE NOTCR        nein, weiter Zeilenbuffer editieren
    

Die ersten 3 Befehle von NEXTCHAR sind genau das Gegenstück zu ECHO, der direkt hier eingebaute zweite Treiber, für die Tastatur/Eingabe. Wieder Status testen, diesmal den echten, vom Status Register KBDCR (= $D011). Ausgabe war warten solange das Terminal Minus=1=belegt ist, bis Plus=0=besetzt wird, hier ist Eingabe warten solange die Tastatur Plus=0=leer ist, bis es zu Minus=1=bereit wird, also ist mit BPL den LDA wiederholen. Warum LDA benutzt wird statt konsistenter BIT als Test ist mir unbekannt. Dann folgt Taste vom Eingabedaten Register an KBD (= $D010) abholen. (Apple II verwendet das passendere BIT dazu. Auch ist dort dieser Treiber separat, mit JSR und RTS aufgerufen. Er heisst dann KEYIN. Dazu kommt erweitert RDKEY, welches zuerst den Cursor anzeigt, was der KEYIN danach stets beseitigt.)

Danach wird die Taste mit STA gespeichert. Dazu hat es bei IN (von $0200 bis $027F) einen 128 Bytes bzw Tastencodes grossen Buffer (Zwischenspeicher) für die Kommandozeile. Das IN,Y beim STA Befehl bedeutet als Adressmodus den 2Byte 16bit als Anfangsadresse $0200 (als 00 02) nehmen plus die 8bit als Position/Index von Hilfsregister Y addieren, hier von 0..127 gehend. Das ist eines der 5 anderen Adressmodi. Die Einstellung von Y kommt noch, sie ist im übersprungenen Code drin.

Der JSR ECHO, geht zum Terminal/Ausgabe Treiber, der das getippte Zeichen auf den Bildschirm weitergibt. Genau daher heisst der Treiber auch ECHO, weil er dazu dient, das was herein kommt wieder hinaus zu geben. Dabei wird ein "_" für Backspace/Backdelete wie jedes andere Zeichen ausgegeben. Ein Escape für Abbruch ebenso, was aber wie jedes Steuerzeichen ausser Carriage Return von der Terminalllogik nicht verwertet wird. Anderseits gibt ECHO auch anderes aus, was nicht herein kam, wäre daher mit PRCHAR besser benamst gewesen. (Apple II ersetzt daher den Namen mit COUT, für CharacterOUT.)

Am Schluss wird auf Taste = Carriage Return getestet, also auf das Enter am Ende der Kommandozeile. Falls gleich diesem wird der BNE durchfallen, ende editieren, gehe weiter zu auswerten. Mit ungleich wird BNE genommen und ist editieren noch nicht zu ende, gehe hinauf zu NOTCT (= $FF0F), also genau nach dem Reset, zu dem was ich erstmals übersprungen habe, und jetzt nach und nach anschaue:

NOTCR:
FF0F   C9 DF      CMP #"_"+$80   ist Taste "_" für Backspace, mit Bit7 = 1
FF11   F0 13      BEQ BACKSPACE    ja, letztes Zeichen vom Buffer vergessen


BACKSPACE:
FF26   88         DEY            Backspace: reduziere Anzahl Zeichen in Y
FF27   30 F6      BMI GETLINE    ist Y < 0, aus Zeilenbuffer gefallen, Neustart
FF29   !!! weiter bei NEXTCHAR   gleich nächste Taste holen


GETLINE:
FF1F   A9 8D      LDA #$0D+$80   Zeilenanfang: Carriage Return neue Zeile
FF21   20 EF FF   JSR ECHO       Zeichen ausgeben
FF24   A0 01      LDY #0+1       Anfang Zeilenbuffer, Y = 0, +1 kompensiere DEY
FF26   !!! weiter bei BACKSPACE  jetzt durch Backspace weiter
    

Der Name NOTCR ist genau das: Alles ausser Carriage Return (CR) landet hier. Erster Test ist auf Taste gleich "_", also Backspace/Backdelete, die Korrekturtaste. Falls es diese war, geht es mit BEQ zu BACKSPACE (= $FF26). Wo das letzte Zeichen "vergessen" wird durch DEY (Y = Y-1) rechnen. Das Zeichen verbleibt im Buffer, es wird einfach durch das nächte getippte Zeichen überschrieben werden. Es hat keine ECHO Ausgabe, da die Terminal Logik kein löschen kann! Und das "_" wurde ja bereits nach einlesen ausgegeben.

Das BMI testet auf Y negativ, also falls es vorher Y = 0 war und nun mit dem DEY zu -1 geworden ist, weil schon davor nichts mehr im Buffer drin war. Falls dies zutrifft wird zu GETLINE (= $FF1F) gegangen. Dort wird ein Carriage Return mit ECHO ausgegeben, als neue Zeile um dem Benutzer den Fehler sichtbar zu machen, dass er nicht mehr in der alten Zeile am editieren ist. Wieder ist es ECHO, trotz dass solche Ausgabe eben kein ECHO mehr ist! Schliesslich wird mit LDY das Y auf 1 gestellt, nicht auf 0, weil sonst das an $FF26 nachfolgende DEY es zu -1 machen würde. So etwas nennt man vorkompensieren. Es kostet 2 weniger Bytes als den BACKSPACE Code zu überspringen.

War die Taste kein Backspace gehen die Tests weiter mit:

       *** weiter in NOTCR
FF13   C9 9B      CMP #$1B+$80   ist Taste ESC für Abbruch, mit Bit7 = 1
FF15   F0 03      BEQ ESCAPE       ja, Zeile abbrechen, "\" und Y = 0


ESCAPE:
FF1A   A9 DC      LDA #"\"+$80   Zeilenabbruch: "\" ausgeben um dies zu zeigen
FF1C   20 EF FF   JSR ECHO       Zeichen ausgeben
FF1F   !!! weiter bei GETLINE    jetzt durch Zeilenanfang und Backspace weiter
    

Zweiter Test ist auf Escape, also Kommadozeile editieren abbrechen Taste. Falls es diese war, geht es mit BEQ zu ESCAPE (= $FF1A) Dieses gibt nur ein \ als Abbruchbestätigung aus mit ECHO, weil Escape ja nicht sichtbar ausgegeben wird, gefolgt von ab $FF1F eine neue Zeile anfangen mit GETLINE wie gehabt. Obiger Y = -1 Fall hat einfach diesen Teilfall direkt ausgenutzt.

Ansonsten gilt für alle anderen Zeichen:

       *** weiter in NOTCR
FF17   C8         INY            nächste Position in Buffer
FF18   10 0F      BPL NEXTCHAR     ist Y < $80, noch Platz im Buffer, weiter
FF1A   !!! weiter bei ESCAPE     jetzt Zeilenabbruch mit ASCII "\" ausgeben
    

Ist die Taste weder Backspace noch Escape wird die Zeichenzahl mit Y = Y+1 gerechnet. Damit wird auch beim nächsten STA IN,Y der nächste Platz im Buffer benutzt werden. Dies aber nur falls Y positiv bleibt, also im Bereich 0..127 ist, und somit noch im Buffer. Auf was der BPL testet, und bei passend geht es zu NEXTCHAR von ganz oben. Versagt dies weil zu 128 geworden fällt er zum obigen ESCAPE durch, für Abbruch, etwas gröber als bei von 0 zu -1 werden, wo nur GETLINE ist, für neue Zeile.

Nach all diesen Einzelteilen, kann man nun sehen wie alles zusammenpasst, und was nach Reset damit passiert, wenn ich alle Editor Codeteile in der ROM Reihenfolge wieder zusammensetze:

NOTCR:
FF0F   C9 DF      CMP #"_"+$80   ist Taste "_" für Backspace, mit Bit7 = 1
FF11   F0 13      BEQ BACKSPACE    ja, letztes Zeichen vom Buffer vergessen
FF13   C9 9B      CMP #$1B+$80   ist Taste ESC für Abbruch, mit Bit7 = 1
FF15   F0 03      BEQ ESCAPE       ja, Zeile abbrechen, "\" und Y = 0
FF17   C8         INY            nächste Position in Buffer, Reset #$7F
FF18   10 0F      BPL NEXTCHAR   ist Y < $80, noch Platz im Buffer, weiter

ESCAPE:
FF1A   A9 DC      LDA #"\"+$80   Zeilenabbruch: "\" ausgeben um dies zu zeigen
FF1C   20 EF FF   JSR ECHO       Zeichen ausgeben

GETLINE:
FF1F   A9 8D      LDA #$0D+$80   Zeilenanfang: Carriage Return neue Zeile
FF21   20 EF FF   JSR ECHO       Zeichen ausgeben
FF24   A0 01      LDY #0+1       Anfang Zeilenbuffer, Y = 0, +1 kompensiere DEY

BACKSPACE:
FF26   88         DEY            Backspace: reduziere Anzahl Zeichen in Y
FF27   30 F6      BMI GETLINE    ist Y < 0, aus Zeilenbuffer gefallen, abbruch

NEXTCHAR:
FF29   AD 11 D0   LDA KBDCR      6820 Port A Status, Test ob bereit mit Eingabe
FF2C   10 FB      BPL NEXTCHAR   Bit7 = 0 ist nicht bereit, warten bis 1 wird
FF2E   AD 10 D0   LDA KBD        6820 Port A, lese Zeichen von Tastatur
FF31   99 00 02   STA IN,Y       zu Zeilenbuffer, $0200..$027F, Y = Position
FF34   20 EF FF   JSR ECHO       Zeichen ausgeben, mit ECHO
FF37   C9 8D      CMP #$0D+$80   ist Taste Carriage Return, mit Bit7 = 1
FF39   D0 D4      BNE NOTCR        nein, weiter Zeilenbuffer editieren
    

Zeichen aufnehmen ist erst ab NEXTCHAR, dann ausser nach Carriage Return zu NOTCR, wo alles auswerten anfängt. Dort kommen die Tests auf Taste war Backspace/Backdelete oder Escape, mit ansonsten INY und dann falls alles gut ging die ESCAPE und GETLINE und BACKSPACE überspringen wieder zu NEXTCHAR, der Normalbetrieb. Scheitert der Platz Test, oder hat der Escape Test schon das INY übersprungen, geht es ab ESCAPE in den vollen Abbruch inklusive GETLINE. Hatte schon der Backspace/Backdelete angeschlagen ist bestenfalls bei BACKSPACE das DEY und dann weiter, oder falls Leertest scheitert ein reduzierter Abbruch ab GETLINE. Womit alles nach dem INY und BPL bis NEXTCHAR von letzterem aus rückblickend angeschaut ein zunehmend grober Abbruch ist.

Sowas sieht auf dem ersten Blick nach Spaghetticode aus, weil ohne Blockstruktur hin und her gesprungen wird. Aber es sind trotzdem geordnete Spaghetti, weil jedes Codefragment darin einem spezifischen Zustand vom Editor entspricht, welcher zwischen den Zuständen ausführen passend herumspringt, dabei in jedem passendes verbleibendes abarbeiten bevor er in den nächstfolgenden Zustand übergeht. Was alles als eine Statemachine bekannt ist (von State = Zustand).

Verbleibt noch der Einstieg. Nach dem Konfigurieren von Prozessor und 6820 kommt es oben bei NOTCR herein. Dabei ist immer noch A = $A7, von der 6820 Konfiguration her. Somit ist bei NOTCR weder $DF Backspace noch $9B Escape, also greift keine der BEQ. Es wird also trotz keine Taste abholen und abspeichern das INY gemacht! Weil aber Y vom 6820 Port B Pins einstellen mit LDY STY statt LDA STA immer noch $7F = 127 ist, wird es jetzt zu 128, was einem "Überlauf" entspricht, und der BPL fällt durch. Womit automatisch der volle "zu grosse Zeile" volle Abbruch kommt. Somit kommt auch das Abbruch \ als Laufendbestätigung ausgben, gefolgt von Neuzeile, sowie das Y = 1 plus DEY mit davon kein BMI, um die leere Kommandozeile erstmals vorzubereiten, gefolgt von auf Eingabe warten.

Dadurch wurde auch die maximale Kommandozeile inklusive dem CR am Ende auf 128 Zeichen definiert, statt 256 (was das Y = 0..255 eigentlich erlauben würde, mit einem BEQ für dem "zu gross" Test), weil deren letzte nutzbare Position 127 zum Pinkonfiguration Muster $7F passt, und so 2 Bytes an LDY spart, bzw eher 2 Bytes an BNE um direkt zu ESCAPE zu springen! Editor erledigt in 44 Bytes. Es verbleiben jetzt noch 205-44=161 Bytes, von $FF3B bis $FFDB.

Warum nicht einfach die NOTCR Tests nach der Eingabe stellen und an $FF0F mit ESCAPE anfangen, ist mir unbekannt, vermutlich auch mehr Bytes. Nur die beiden CMP und BEQ nach dem Zeichen ausgeben wäre sicher keine Differenz, und der A Inhalt wäre so irrelevant. Aber die INY und BPL vor den CMP #$0D+$80 und BNE liegend könnten in Konflikt kommen, was einen BNE seine 2 Bytes mehr bräuchte, oder ohne dies Y weiterhin doppelt nutzen muss.

(Apple II hat ohne diesen Trick volle 256 Zeichen inklusive dem CR als Kommandozeile Ende. Ebenso ist der ganze Kommandozeileneditor eine mit JSR und RTS benutzbare Hilfsfunktion, namens GETLN. Dieses zeigt auch einen Prompt an, der verschieden ist je nach was das aufrufende Program benutzt, Monitor "*", Mini-Assembler "!", Integer Basic "<", Applesoft Basic "]", Basic Benutzeringaben "?".)

Kommandozeichen auswerten

Danach ist die Kommandozeile fertig editiert. Was geschieht nun nach einem Carriage Return? Dann geht es nach dem nicht genommenen BNE weiter zu $FF8B, Kommandozeile auswerten. Zuerst kommen dazu etwas Vorbereitungen:

       *** weiter in NEXTCHAR
FF3B   A0 FF      LDY #0-1       Anfang Zeilenbuffer, Y = 0, -1 kompensiere INY
FF3D   A9 00      LDA #0         A = 0 für X setzen und anfangs Hexmodus
FF3F   AA         TAX            X = A für anfangs Hexzahl (später)

SETSTOR:
FF40   0A         ASL A          kein Effect bei A = 0, 2*$BA=$174=$74 bei ":"

SETMODE:
FF41   85 2B      STA MODE       Hexzahl verarbeiten Modus, 0 = Ausgabe Adresse
    

Bisher war in Y die Anzahl Zeichen und deren Position im Buffer eingeben. Nachdem das Carriage Return gespeichert wurde, ist das Ende "fixiert", wird Y frei, und wird jetzt für die Position um Zeichen auszuwerten statt abzuspeichern benutzt, also wieder ab 0. Was aber wegen dem kommenden INY in $FF43 mit -1 vorkompensiert wird. A wird 0, mit dem folgenden ASL bleibt es immer noch 2*0=0, und wird abgespeichert in Variable MODE (= $2B) in der Zeropage. Damit wird später nach Einlesen einer Hexzahl entschieden was damit zu machen ist. Mit 0 wird diese als Adresse angeschaut, und sie als Position gesetzt und mit ihren Inhalt ausgeben. Das TAX ist nur um bei dieser Gelegenheit in 1 Byte das X mit 0 zu löschen, statt es später mit LDX #0 in 2 Bytes zu machen. X wird per Konvention im Monitor immer auf 0 gelassen, oder falls anderst benutzt wieder zu 0 restauriert.

Dann muss ein Zeichen geholt und ausgewertet werden:

BLSKIP:
FF43   C8         INY            nächstes Zeichen, -1 wird zu 0 = erstes

NEXTITEM:
FF44   B9 00 02   LDA IN,Y       von Zeilenbuffer, $0200..$027F, Y = Position
FF47   C9 8D      CMP #$0D+$80   ist Taste Carriage Return, mit Bit7 = 1
FF49   F0 D4      BEQ GETLINE      ja, Carriage Return ausgeben, neue Eingabe
FF4B   C9 AE      CMP #"."+$80   ist Taste "." für Endadress, mit Bit7 = 1
FF4D   90 F4      BCC BLSKIP       kleiner, vielleicht Leerzeichen, ignorieren
FF4F   F0 F0      BEQ SETMODE      ja, neuer Hex Modus, $AE = Bereich Ende
FF51   C9 BA      CMP #":"+$80   ist Taste ":" für Eingabe, mit Bit7 = 1
FF53   F0 EB      BEQ SETSTOR      ja, neuer Hex Modus, 2*$BA=$74 = schreiben
FF55   C9 D2      CMP #"R"+$80   ist Taste ASCII "R", mit Bit7 = 1
FF57   F0 3B      BEQ RUN          ja, starte Programm an Adresse
    

Bisher hatten wir beim Editieren STA IN,Y und dann INY, mit Y anfangs 0, was Postincrement genannt wird. Hier ist es INY und dann LDA IN,Y, mit Y anfangs -1, was Präincrement genannt wird. Dies wirkt fast gleich, aber siehe weiter unten für zwei gewollte Seiteneffekte. Der erste Test, auf ein Carriage Return finden bedeutet die Zeile ist fertig abgearbeitet, gehe neue holen, daher mit BEQ wieder ab nach GETLINE, was wir bereits kennen.

Eine Hexzahl einfach so ist eine Adresse, aber nach "." eine Endadresse und nach ":" eine Dateneingabe. Hier wird daher auf "." bzw ":" getestet und dieses vermerkt, durch bei erstem BEQ zu SETMODE (= $FF41) hinauf zum STA $2B, oder bei zweitem BEQ zu SETSTOR (= $FF40) hinauf zum ASL A und dann erst STA $2B. Also wird das Kommandozeichen selber zur neuen Hexzahl Modusnummer, statt normal auf 0 gesetzt sein. Bei "." = $AE wird direkt abgespeichert. Das ASL ist eine Multiplikation mal 2, hier wird es benutzt um per Seiteneffekt aus ":" = $BA mit *2 ein $174 zu machen, von dem aber wegen 8bit nur die $74 verbleiben und abgespeichert werden. Wonach gilt bei Hexzahl: Adressmodus = 0, Endadressmodus = $AE, Schreibmodus = $74. Auch dies ist eine Form von Statemachine, aber mit den Zustand extern in der Variable MODE gehalten statt direkt im angesprungenen Codefragment seiner Adresse. Das weil zuerst vom Zustand unabhängiger Code dran sein wird, um die Hexzahl zu lesen, und erst danach der Zustand relevant wird.

Das eine BCC nach dem "." Test sorgt mit Sprung nach $FF43 dafür, dass alle Zeichen mit Codes unterhalb von "." einfach fallengelassen werden, durch mit nächstem Zeichen weitermachen. Das beinhaltet gewollt die Leerzeichen als Adressen/Kommandos bzw Datenbytes Trenner, aber ebenso werden alle !"#$%&'()*+,- als "Leerzeichen" behandelt, ein undokumentiertes Feature! Dies vermeiden würde einen expliziten CMP #" "+$80 plus BEQ statt dem BCC brauchen, also 2 Bytes gespart.

Alle diese Varianten nutzen erneut das eine INY vor dem LDA, was mehrere solche Bytes einspart, erster gewollter Seiteneffekt! Der Name BLSKIP bedeutet vermutlich "Blank Skip" und ist damit bei "Leerzeichen" richtig benamst, aber nach "." bzw ":" unpassend, wäre mit NEXTREAD besser benamst. Auch das ASL vor dem Hexzahl Modus STA spart 2 Bytes, weil sonst nach dem ":" Test ein BNE notwendig wäre um bei nicht-":" ein ASL hier zu überspringen. Oben hatte ASL bei Modus 0 keinen Effekt, konnte einfach durchlaufen werden.

Weiter bedeutet ein "R" als Kommando, dass ein Programm ab Adresse ausgeführt werden soll. Da "R" gewisse Vorarbeiten braucht, welche beim davor verarbeiteten Adresse eingeben Befehl bereits geschahen, lasse ich den "R" Fall an RUN (= $FF94) weiter verfolgen momentan weg, und hole ihn ganz am Schluss nach. Damit sind wir bereits alle Kommandozeichen durch. Interpreter erledigt in 30 Bytes. Es verbleiben jetzt noch 161-30 = 131 Bytes, von $FF59 bis $FFDB, und somit sind wir halbwegs durch.

Hexzahl konvertieren

Ist es kein Zeile fertig oder Kommandozeichen oder Datenbytes Trenner kann nur noch eine Hexzahl dran sein, oder Fehler. Wieder kommen zuerst etwas Vorbereitungen um eine solche zu konvertieren:

       *** weiter in NEXTITEM
FF59   86 28      STX L          Hexzahl untere Hälfte = 0
FF5B   86 29      STX H          Hexzahl obere Hälfte = 0
FF5D   84 2A      STY YSAV       merke aktuelle Position Y im Buffer
    

Zuerst wird mit beiden STX das X = 0 von $FF3F oben in Variablen L (= $28) und H (= $29) gespeichert, 2 Bytes welche die zu entstehende maximal 16bit grosse Hexzahl aufnehmen werden. Eine ziffernlose Zahl hat damit einfach den Wert 0! Damit geschieht automatisch auch die Erweiterung von 1..3 ziffrigen Zahlen mit vorangestellten Nullziffern auf 4 ziffrig. Mit dem STY wird die aktuelle Position in der Kommandozeile in Variable YSAV (= $2A) gemerkt, um später zu testen ob Ziffern verdaut wurden, und somit überhaupt eine Zahl vorlag!

Dann wird auf gültige Ziffern getestet:

NEXTHEX:
FF5F   B9 00 02   LDA IN,Y       von Zeilenbuffer, $0200..$027F, Y = Position
FF62   49 B0      EOR #"0"+$80   nicht-SBC "Subtraktion" von "0", mit Bit7 = 1
FF64   C9 0A      CMP #9+1       ist Resultat <= 9, und so eine Dezimalziffer?
FF66   90 06      BCC DIG          ja, addiere diese Ziffer ohne Korrektur
FF68   69 88      ADC #$FA-$71-1 "A".."F" waren $C1..$C6, EOR machte $71..$76
                                 schiebe $71 zu $FA, aber ADC Carry=1 also -1
FF6A   C9 FA      CMP #$FA       ist Resultat < $FA..$FF, so nicht Ziffern A..F
FF6C   90 11      BCC NOTHEX       ja, keine Hexziffer, fertig Zahl Konvertiert
    

LDA holt erstes/nächstes Zeichen von der Zahl. Weil INY vor dem obigen LDA war, und nicht danach, ist Y unverändert, die erste Ziffer holt einfach wieder das gleiche Zeichen, zweiter gewollter Seiteneffekt!

Das EOR ist genau Gegenstück zum ORA in PRHEX, wieder statt 3 Bytes SED und SBC nur 2 Bytes EOR. Auch der Test auf <= 9 (genauer auf < 9+1) ist das Gegenstück zu dort. Ebenso mit BCC weiter zu $FF6E falls 0..9. Auch hier wird mit einem ADC korrigiert, aber weit eigenartiger. PRHEX hat nur 7 Zeichen übersprungen. Hier werden dagegen die Ziffern A..F zu 250..255 geschoben, weil danach diese später noch mit -240 abschneiden zu 10..15 gewandelt werden. Wieder gilt dass ein Carry=1 gegeben ist, also muss die Konstante um -1 korrigiert werden. Ist die Ziffer nun unter 250, ist es damit kein A..F. Das weil auch alles G..oberstes nun zwar über 255 wären, aber damit nach Verlust von 256 wegen 8bit Prozessor seinem Zahlenbereich Überlauf, auch unter 250 fallen. Genau daher wurden die A..F dort hinauf verschoben! War es keine Ziffer mehr bricht der BCC aus nach $FF7F, dem einzigen Ausgang dieser Schleife. Dies ist eindeutig dar eigenartigste Algorithmus im ganzen Monitor!

Haben wir nun eine Ziffer, wird diese zur entstehenden Hexzahl addiert, was das Gegenstück zu PRBYTE seinem zerlegen ist, allerdings hier mit einer 16bit Zahl, nicht nur 8bit. Hier muss es, weil spätere Hexziffern eine niedere Wertigkeit haben, nach z.B. bereits eine 1 und 2 vorhanden sein eine weitere 4 daraus die Zahl 12*10+4 = 124 machen. Weshalb die bestehende Hexzahl mit *16 nach links verschoben werden muss und dann die neue Hexziffer hinten addiert werden. Dazu muss die neue Hexziffer wegen 4bit auf 8bit Prozessor vorher nach links plaziert werden:

DIG:
FF6E   0A         ASL A          schiebe rechte/LSB Hexziffer nach links/MSB
FF6F   0A         ASL A            braucht 4 mal 1 bit schieben
FF70   0A         ASL A            löscht links/MSB $F0 von A..F Ziffern
FF71   0A         ASL A
    

Die 4 ASL entsprechen genau den 4 LSR vom PRBYTE, nur eben *2 statt /2 Gegenstücke. Danach steht die Ziffer in der linken Hexadezimalstelle von A bereit. War die Ziffer 0..9 wurden die Bytes 00..09 vom EOR nun geschoben zu 00..90, die linke 0 ging verloren. War die Ziffer A..F wurden nun die Bytes FA..FF vom ADC geschoben zu A0..F0, die linke F wurde so abgeschnitten, was auch gleich das erwähnte -240 ergibt!

FF72   A2 04      LDX #4         starte Schleife, für 4 Bits pro Hexziffer

HEXSHIFT:
FF74   0A         ASL A
FF75   26 28      ROL L          Hexzahl untere Hälfte, neu = alt *16 + A
FF77   26 29      ROL H          Hexzahl obere Hälfte, neu = alt *16 + A
FF79   CA         DEX
FF7A   D0 F8      BNE HEXSHIFT   Schleife 4 mal
FF7C   C8         INY            Ziffer wurde verarbeitet, nächste Ziffer
FF7D   D0 E0      BNE NEXTHEX    Schleife immer, nächstes Zeichen holen
    

Das LDX sorgt nur dafür, dass alles nach HEXSHIFT 4 mal stattfindet. Dessen ASL *2 schiebt stets das oberste Bit der neuen Ziffer aus A hinaus, ins Carry, einem 1bit Hilfsregister welcher bei Rechnungen der Sorte A = A (0..255) + Daten (0..255) + Carry (0..1) = 0..511, bei allen Ergebnissen oberhalb von A = 0..255, wo sonst 256 verloren gehen würden, auf 1 gesetzt wird, womit es zum neunten Bit mit Wert 256 wird. Dieser wird von ADC (und SBC) sowie ASL (und LSR) gesetzt, und kann neben von ADC (und SBC) auch von ROL (und ROR) ausgewertet werden. Wobei ROL genau das selbige macht wie ASL, nur eben neben dem *2 noch das Carry 0 oder 1 dazu addieren. Womit die ASL ROL ROL Folge die ganze Gruppe A,$28,$29 um 1 Bit verschiebt, also 3*8=24bit Arithmetik macht, mit wegen zuvor nach links plaziert 16alt+4neu=20bit an Nutzdaten darin.

Das DEX macht X = X-1, und BNE tut bei solange nicht X = 0 ab $FF74 wiederholen, was nach 4 mal -1 dann nicht mehr geschieht, und somit eine typische Abzählschleife ergibt. Insgesammt werden so alle 4 linken Hexziffer Bits aus A nach Carry und dann in L geschoben, dessen unteren 4 nach oben, dessen oberen 4 nach Carry und dann in H, dessen untern 4 nach oben, dessen oberen 4 gehen verloren. Womit auch zuviele Hexzahlenbits "vorne" herausgeschoben werden. Genau daher kann man nach Fehlern in Adresse (oder Daten) tippen einfach 4 (bzw 2) weitere richtige Ziffern eingeben, und nur diese bleiben erhalten, wieder ein Seiteneffekt. Das ganze sieht als Bits so aus:

Eine Hexzahl 1248 eingeben wird so zu:

erste Ziffer 1 in A
  L=$29      C      H=$28      C        A
0000'0000 <- ? <- 0000'0000 <- ? <- 0001'0000
                                    ^^^^

4 mal ASL ROL ROL
   ROL               ROL               ASL
0000'0000 <- 0 <- 0000'0000 <- 0 <- 0010'0000
0000'0000 <- 0 <- 0000'0000 <- 0 <- 0100'0000
0000'0000 <- 0 <- 0000'0000 <- 0 <- 1000'0000
0000'0000 <- 0 <- 0000'0001 <- 1 <- 0000'0000
                       ^^^^
zweite Ziffer 2 in A
0000'0000 <- ? <- 0000'0001 <- ? <- 0010'0000
4 mal ASL ROL ROL                   ^^^^
0000'0000 <- 0 <- 0001'0010 <- 0 <- 0000'0000
                       ^^^^
dritte Ziffer 4
0000'0001 <- 1 <- 0010'0100 <- 0 <- 0000'0000
                       ^^^^
vierte Ziffer 8
0001'0010 <- 1 <- 0100'1000 <- 0 <- 0000'0000
                       ^^^^
   1    2            4    8
    

Der INY geht wie gehabt zum nächsten Zeichen weiter, und wird daher mindestens 1 und maximal 127 sein. Daher wird der zweite BNE immer wiederholen, zu $FF5F hinauf gehen und nächstes Zeichen mit LDA holen und auswerten ob Ziffer. Damit ist auch dieses INY fast direkt vor einem LDA, wenn auch nichtlinear angeordnet! Das BNE so ausgenutzt braucht nur 2 Bytes, statt einem 3 Byte JMP welches immer springt. (Die 6800 hat ein BRA das in 2 Bytes immer springt. 8080 kennt dagegen nur 3Byte 16bit Adresse Sprungbefehle, für bedingt und immer springend.)

Fehler erkennen

Einziger Weg aus der Zahlenverarbeitung heraus ist somit das BCC NOTHEX in $FF6C bei auffinden einer Nicht-Hexziffer. Danach folgen Tests was mit der entstandenen Hexzahl anzustellen ist:

NOTHEX:
FF7F   C4 2A      CPY YSAV       vergleiche Y mit Anfangsposition im Buffer
FF81   F0 97      BEQ ESCAPE     keine Ziffern, war keine Zahl, abbrechen
    

Der Name NOTHEX ist wie NOTCR genau das. Das erste nicht Hexziffer Zeichen landet hier, egal on Zahl zu Ende ist oder gar keine Zahl war. Das CPY vergleicht das aktuelle Y mit der einst bei $FF5D in YSAV gemerkten Position, bevor eine Zahl konvertieren versucht wurde. Sind beide gleich, war gar keine Ziffer dort, es wurde sofort hierher gesprungen, also war keine Zahl. Also war es weder Zeile fertig, noch ein Datenbytes Trenner, noch ein bekanntes Kommandozeichen, noch eine Ziffer, und muss folglich ein Tippfehler gewesen sein. Also wird mit dem BEQ abgebrochen, ab nach ESCAPE, wie nach einem Zeilenbuffer voll oder Escape beim editieren, mit dessen \ als volle Abbruchbestätigung, gefolgt von neuem Zeilenanfang. Zahlen einlesen erledigt in 42 Bytes. Es verbleiben jetzt noch 131-42 = 89 Bytes, von $FF83 bis $FFDB.

Damit entsteht ein eigenartiges Verhalten: Da bereits die ganze Zeile eingetippt ist, mitsammt Carriage Return, kommt das \ auf die Zeile danach, nur als Angabe dass ein Fehler war, aber nicht wo! Der Benutzer muss die Zeile nach dem real fehlgetippten Zeichen absuchen. Und wissen dass alle Kommandos davor bereits ausgeführt wurden, aber das fehlerhafte und alle danach nicht. Gefolgt von letztere ohne Fehler wiederholen. Mit z.B. obiges Demoprogrämmchen falsch getippt, im vierten Byte EG statt EF, sowie danach ab dort korrigiert:

Benutzer:   300: A9 8D 20 EG FF A9 C1 20 EF FF 4C 1F FF
Rechner:    0300: 00\
Benutzer:   303: EF FF A9 C1 20 EF FF 4C 1F FF
Rechner:    0303: 0E
    

Was ist da genau geschehen? Zuerst hat das 300 zum 0300: 00 ausgeben geführt. Dann hat das : den Hexmodus auf Speichern gestellt. Dann wurden A9 8D 20 abgespeichert. Dann ist EG gescheitert und hat das \ erzeugt. Der Rest wurde ignoriert. Man beachte, dass dieser Test aber nur ungültige Tasten abfangen kann. Ein ED statt EF hätte einfach falsche Daten im Speicher abgelegt, ein defektes Programm ergebend. Auch ein ER wäre trotz keine Hexzahl sein gültig, mit zuerst Zahl einlesen beim R abbrechen, dann das E weil eine Ziffer vorhanden als 0E angeschaut und so abgespeichert, und danach gleich das R als Kommando ausgeführt, mit dem defekten und unvollständigen Programm, gefolgt von Absturz! Auch bei diesem EG wird das zuerst auf G abbrechen, dann 0E abspeichern, dann erst das G als Kommando nicht erkennen, dann als weitere Hexzahl versuchen, erst dann mit 0 Zeichen verdaut scheitern. Weshalb auch mit 303: die Adresse neu setzen nötig ist, mit nur : würde ab 304 weiter abspeichern, mit in 303: das falsche 0E belassen! Dieses wird hier wegen mit 303: Adresse neu setzen auch noch ausgegeben.

Hexzahl verwerten

War es eine Zahl, muss diese jetzt verwertet werden:

       *** weiter in NOTHEX
FF83   24 2B      BIT MODE       Test Hexzahl verarbeiten Modus, $74 = speicher
FF85   50 10      BVC NOTSTOR      nein, ist $00 = ausgeben oder $AE = Block

NOTSTOR:
FF97   30 2B      BMI XAMNEXT    Test Hexzahl weiter, da nicht speichern
                                   falls $00 = ausgeben, sonst $AE = Block aus
    

Dazu wird mit BIT jetzt der zuvor gesetzten Hexzahlen verarbeiten Modus getestet. Dabei werden die Bits 6 und 7 vom abgespeicherten Modus in MODE, Bit6 entscheidet ein BVC bzw BVS, Bit7 entscheidet ein BPL bzw BMI. Falls es nach ":" wegen Hexzahl Modus ein $74 mit Bit6=1 hat, wird der BVC scheitern, gleich bei $FF87 weitergehen (zu ein Datenbyte speichern), sonst wird es zu NOTSTOR (= $FF97) gehen. Die $FF87 bis $FF96 werden jetzt ausgelassen, also weiter schauen bei $FF97. Falls es nach "." ein $AE mit Bit7=0 hat, wird der BMI zu XAMNEXT (= $FFC4) gehen (und Daten bis an Endadresse ausgeben). Sonst bei weder ":" noch "." gleich bei $FF99 weitergehen (und somit Adresse setzen und dessen Daten ausgeben). Womit diese Statemachine nun von Zustand extern in einer Variable gehalten zu direkt im angesprungenen Codefragment seiner Adresse wechselt.

Auf den ersten Blick könnte man statt BMI auch BNE benutzen, zumal nach BVC schon erledigt hier der Test nur noch auf $00 oder eben nicht ist. Aber da macht der BIT Befehl einen Strich durch die Rechnung! In der 6800 hatte es separate BIT und TST Befehle, mit BIT eine testweise AND Rechnung mit Akkumulator und Speicher machen, aber TST nur eine Zahl im Speicher oder Akkumulator abtesten. Bei der 6502 hat es nur BIT, als ein Gemisch der beiden. Die BVC/BVS und BPL/BMI Bedingungen werden wie beim 6800 TST abgetestet, was wir hier benutzen wollen, aber die BEQ/BNE Bedingung wird wie beim 6800 BIT mit AND mit dem Akkumulator getestet. (Die PDP-11 hat auch separate BIT und TST, die 6800 hat diese von dort kopiert.)

Daher geht mit BNE auf beliebige Bits gesetzt sein testen nicht, weil BIT für BEQ/BNE ihren AND im Akkumulator einen Wert braucht, weshalb dazu zuerst ein LDA #$FF notwendig gewesen wäre, 2 Bytes mehr. Da der BMI aber nur von Bit7=1 abhängt, welches direkt vom "." als #$2E+$80 in $28 abspeichern herkommt, reicht dieses. Das ist vermutlich auch der Grund für die eigenartige Konvention dass alle Zeichen mit Bit7=1 benutzt werden! Was darauf hinausläuft, dass das Bit7 auf der Tastatur auf 5V=1 verdrahten nur dazu dient, um diesen kleineren Test zu erlauben, der Rest aus Konsistenz dazu folgte. Das ist das mit Abstand schrägste am ganzen Design! Und es war auch das wofür ich am längsten brauchte um den Zusammenhang zu erkennen und verstehen.

(Beim Apple II ist der Grund für Bit7=1 benutzen dagegen definitiv bekannt: Ohne eine PIA 6820 wird das Eingabestatus als Bit7 in den Eingabedaten gelesen, wie im Apple 1 der Ausgabestatus als Bit7 in den Ausgabedaten. Und dieses hat 1=gültig. Das loswerden würde ein AND #$7F kosten, 2 mehr Bytes. Aber diese Logik ist hier irrelevant, mit dem separaten Status Register der 6820.)

Kommando Adresse

Da Adresse eingeben der erste vom Benutzer genutzte Fall ist, und alle anderen erst vorbereitet, lasse ich den direkten speichern Fall an $FF87 weiter verfolgen momentan weg. Folglich werden wir zuerst Adresse setzen anschauen:

       *** weiter in NOTSTOR
FF99   A2 02      LDX #2         starte Schleife, für 2 Bytes pro Adresse

SETADR:
FF9B   B5 27      LDA L-1,X      benutze Hexzahl, beide Hälften
FF9D   95 25      STA STL-1,X    setze speichern Adresse
FF9F   95 23      STA XAML-1,X   setze anzeigen Adresse
FFA1   CA         DEX
FFA2   D0 F7      BNE SETADR     Schleife 2 mal
    

Wieder mal eine von LDX gesteuerte Abzählschleife, alles nach SETADR, dieses nur 2 mal. Dabei wird die Hexzahl von L und H sowohl nach STL (= $26) und STH (= $27) wie auch nach XAML (= $24) und XAMH (= $25) kopiert. Der Grund für dieses Doppel ist, dass man mit StartAdresse: Daten ... R den Anfang setzen kann, dann während den Daten schreiben STL und STH verändert werden, aber danach steht in XAML und XAMH immer noch der Anfang, den das R direkt nutzen kann, statt dazu separate Zeile StartAdresseR eingeben müssen. Die Schleife sind 11 Bytes, einfach 2 mal LDA STA STA linear wären 12, also wieder ein Byte gespart. Ohne separate STL und STH wären es mit nur LDA STA nur 8 Bytes, dieses Komfortfeature kostet also 3 Bytes!

Grund für die -1 überall in den Adressmodi ist, dass X ja 2 bzw 1 als Index drin hat, und nicht 1 bzw 0, weil der DEX erst nach kopieren stattfindet. Den DEX vor kopieren würde dessen Nulltest verlieren, weil LDA auch einen solchen auf die geladenen Daten macht! Mit LDX #1 anfangen, würde mit nach dem DEX ein BPL benutzen gehen, aber würde dann X = -1 statt X = 0 zurücklassen, und danach ein extra INX brauchen um X auf 0 zu holen, was später gebraucht wird. Also wäre so auch kein Byte gespart.

Danach wird die Adresse ausgegeben:

NXTPRINT:
FFA4   D0 14      BNE PRDATA     nicht gesetzt, Adresse nicht ausgeb, nur Daten
FFA6   A9 8D      LDA #$0D+$80   Carriage Return, neue Zeile, mit Bit7 = 1
FFA8   20 EF FF   JSR ECHO       Zeichen ausgeben
FFAB   A5 25      LDA XAMH       Anzeigen Addresse obere Hälfte
FFAD   20 DC FF   JSR PRBYTE     Byte als zwei Hexziffern ausgeben
FFB0   A5 24      LDA XAML       Anzeigen Addresse untere Hälfte
FFB2   20 DC FF   JSR PRBYTE     Byte als zwei Hexziffern ausgeben
FFB5   A9 BA      LDA #":"+$80   ":" als Adresse/Daten Trenner, mit Bit7 = 1
FFB7   20 EF FF   JSR ECHO       Zeichen ausgeben
    

Der BNE ist unerwartet. Er sorgt dafür dass dieser Programmabschnitt Adresse ausgegeben fakultativ sein kann. Nach Adresse setzen, und dabei DEX ein X = 0 machen, wird die Adresse stets ausgegeben werden. Aber falls NXTPRINT von anderswo her aufgerufen wird, ist die Ausgabe fakulativ, je nach ob die letzte Rechnung dort 0 ergab oder nicht. Wir werden später sehen, dass dies nur von einem einzelnen Ort her geschieht, und der BNE ohne mehr Bytes zu brauchen gleich dort hätte sein können, also hier nur Taktzyklen verschwendet und Code weniger lesbar macht!

Die eigentliche Ausgabe ist geradeaus: Ein CR um die neue Zeile mit der Adresse und ihre Daten anzufangen. Daher erscheinen bei mehrere Adressen als Kommandos ihre Ausgaben auch auf separaten Zeilen. Dann wird die Adresse wegen 16bit auf einem 8bit Prozessor in zwei Hälften ausgegegen, jede mit einem PRBYTE. Dann kommt noch das ":" nach der Adresse ausgeben.

Danach kommt das Datenbyte dieser Adresse ausgeben:

PRDATA:
FFBA   A9 A0      LDA #" "+$80   " " als Datenbyte Trenner, mit Bit7 = 1
FFBC   20 EF FF   JSR ECHO       Zeichen ausgeben
FFBF   A1 24      LDA (XAML,X)   Byte von der Adresse (X = 0)
FFC1   20 DC FF   JSR PRBYTE     Byte als zwei Hexziffern ausgeben
    

Ein Leerzeichen wird vor jedem Datenbyte ausgegeben, um es von Adresse oder vorherigem Datenbyte zu trennen, daher nicht erst danach. Generell gilt im Monitor eine Konvention, dass am Anfang von etwas Platz geschaffen wird. Sei das am Anfang der Eingabe eine neue Zeile, am Anfang der Adresse ausgeben eine neue Zeile, oder am Anfang eines Bytes ausgeben ein Leerzeichen (oder wie später gesehen gar eine neue Zeile mit Adresse). Am Schluss wird die Cursorposition stehen gelassen, weil das was danach kommt schon passend neuen Platz machen wird! Daher müssen Programme das selbige machen, am Anfang neue Zeile, am Schluss aber nichts. Dies ist auch analog zu INY direkt vor LDA benutzen.

(Dies im Kontrast zu moderner Software. Wo als Konvention gilt dass Ausgaben sich selber abschliessen, dem Nachfolgenden ihren Ort fest vorgebend. Sowie ebenso INY-artiges als Postincrement nach LDA-artigem kommt, dem Nachfolgenden ihren Ort fest vorgebend.)

Der LDA mit (XAML,X) ist wie die STA und LDA mit IN,Y ein berechneter Adressmodus. Hier werden mit X = 0 als wirkungsloser Index addiert die beiden Adressen XAML und XAMH benutzt (es hat keinen nur (XAML) Adressmodus ohne Index (erst die 65C02 hat das). Daher gilt im Monitor auch die Konvention, dass X = 0 der Normalzustand ist, auch um solches adressieren nicht zu beeinflussen, sowie damit Hexzahlen mit 0 anfangen, und um Hexzahlen Modus auf zu 0 setzen. Weshalb auch mit LDX gesteuerte Schleifen stets mit 0 enden müssen. In XAML und XAMH befindet sich nach obigem Adresse setzen durch dorthin kopieren, die Adresse des Bytes zum Ausgeben. Was nun so als indirekte Adresse zu den tatsächlichen Daten geht, was eine Pointer Dereferenzierung ergibt.

(Da diese indirekte Adressierung und eine weitere mit Y nur mit Zeropage funktioniert, sind diese beiden Adressenmodi der Grund warum dort RAM sein muss. Dazu kommen JSR und RTS als Grund warum in der Stackpage RAM sein muss. Das sind die beiden Prozessorfunktionen welche sonst versagen.)

Kommando Endadresse

Eigentlich ist das Adresse Kommando nun erledigt. Aber da es auch ein StartAdresse.EndAdresse Kommando gibt, und dieses den selbigen PRDATA Datenbyte ausgeben Programmteil benutzt, sowie nach jedem achten Byte auch den selbigen NXTPRINT, nutzen beide den selbigen Code! Daher muss nun ein Test auf die Endadresse kommen. Bei der Startadresse von StartAdresse.EndAdresse wurde ebenfalls XAML und XAMH auf den Start gesetzt, von der Hexzahl in L und H her kopiert. Dann wurden sofort diese und ihre Daten ausgegeben. Erst danach kam überhaupt den "." lesen kommen und der Hexzahlen Modus geht dabei von 0 auf $AE. Dann kam die Endadresse lesen kommen, und diese ist nun in L und H, aber nicht zu XAML und XAMH kopiert, wegen dem BMI XAMNEXT bei $FF97, der die bereits gemachten (Start-)Adresse Sachen übersprungen hat, zu hierher. Danach werden die Bytes nach der Adresse in XAML und XAMH durchgegangen bis und mit bei der Adresse in L und H angekommen.

Bei nur Adresse Kommando, und auch nach der StartAdresse, sind dagegen die L und H immer noch gleich wie XAML und XAMH, also reicht der selbige Endadresse Test, weil "schon erreicht". Falls StartAdresse.StartAdresse eingegeben würde, wird letzterer Teil einfach abbrechen, ohne je etwas zu machen, weil alles schon gemacht ist! Beim nur .EndAdresse Kommando wird wiederum durch das BMI XAMNEXT genau hierhin gesprungen, weil die Adresse und das erste Byte ja bereits ausgegeben sind. Damit ist jedes weitere .EndAdresse nur eine Erweiterung, selbst mit StartAdresse direkt davor als StartAdresse.EndAdresse sind das strikte 2 Kommandos, Adresse und .EndAdresse, egal ob zusammen oder mit Leerzeichen getrennt, oder auf zwei separaten Kommandozeilen!

Also wird nun getestet auf Endadresse schon erreicht:

XAMNEXT:
FFC4   86 2B      STX MODE       Hexzahl verarbeiten Modus, wieder auf 0
FFC6   A5 24      LDA XAML       Vergleiche aktuelle Addresse mit Hexzahl
FFC8   C5 28      CMP L
FFCA   A5 25      LDA XAMH
FFCC   E5 29      SBC H
FFCE   B0 C1      BCS TONEXTITEM Endadresse erreicht, zurück zur Zeile
    

Da dies auch der Einsprungpunkt für .Endadresse ist, wird zuerst mit dem STX MODE der Hexzahlen Modus wieder auf 0=Adresse gestellt, damit die nächste Hexzahl wieder als (Start-)Adresse gewertet wird! Nach einem Adresse oder StartAdresse war es schon 0, bleibt einfach gleich. Dann werden die unteren Hälften in XAML und L verglichen, dann obere in XAMH und H, letztere mangels Übertrag beim CMP mit SBC. Der BCS bricht ab, falls Ende erreicht ist.

Eigentlich wollen wir dann zurück zu NEXTITEM an $FF44, um die Kommandozeile weiter abzuarbeiten. Aber die Distanz von $FFD0 zu $FF44 = -$8C = Dezimal -140 ist dem BCS etwas zu weit, er kann ja maximal -128 (und +127). Also wird ein ohnehin nach der Speicherroutine bei $FF91 liegender weiterer Sprung nach $FF44 angesprungen, was in einem "Zweisprung" resultiert. Das ist eine normale Methode um auf 8bit Prozessoren mehr Distanz zu bekommen. (6800 macht das auch so. 8080 kann ohnehin nur volle 3Byte 16bit Adresse Sprungbefehle, mit beliebiger Distanz.)

Falls ungleich Endadresse wird das nächste Byte ausgeben nun vorbereitet. Dies auch bei .EndAdresse Kommando, ohne bisher etwas ausgegeben zu haben, da das erste Byte schon vom StartAdresse ausgegeben wurde. Das braucht nur:

       *** weiter in XAMNEXT
FFD0   E6 24      INC XAML       nächste Adresse untere Hälfte
FFD2   D0 02      BNE MOD8CHK    keine Pagegrenze überschritten
FFD4   E6 25      INC XAMH       nächste Adresse obere Hälfte
    

Falls nicht Endadresse, werden die INC benutzt um die Adresse +1 zu rechnen. Zuerst XAML = XAML + 1. Aber dann falls das am Ende einer Page von 255 zu 0 überschritten wurde und so der BNE durchfällt, auch noch XAMH = XAMH + 1 für in die nächste Page. Das ist die normale Methode um auf einem reinen 8bit Rechner 16bit Adressen zu rechnen.

(Hier haben 6800 und 8080 dedizierte 16bit +1 und -1 Befehle (und auch 16bit LDX #). Die 6800 aber nur auf ihr Hilfsregister, sowie die 8080 nur paarweise in ihren 6 Bytes im Prozessor. Die 8008 kann dagegen nur 8bit, wie oben, und das zudem nur in ihren 6 Bytes im Prozessor, von denen auch noch nur ein Paar Adresse sein kann.)

Da nun weitere Daten ausgeben werden, kommt wieder die "am Anfang einer Ausgabe Platz schaffen" Konvention zum Zug. Bei beliebigen Bytes reicht das PRDATA Leerzeichen vor den Daten. Aber falls bereits 8 Bytes ausgegeben wurden, eine Zeile voll ist, braucht es mehr, auch das NXTPRINT für CR und Adresse und ":" ausgeben. Welches der Fall ist wird nun getestet:

MOD8CHK:
FFD6   A5 24      LDA XAML
FFD8   29 07      AND #$07       Test erstes=0 or anderes=1..7 Byte in Zeile
FFDA   10 C8      BPL NXTPRINT   Schleife immer, erstes mit Adresse, sonst ohne
    

Dies testet die neue Adresse ihren XAML Anteil darauf, ob die niedrigsten 3 Bits, mit dem AND ausgefiltert, 0 oder 1..7 sind. Das ergibt eine Modulus Division durch 8, und wird benutzt um die Ausgabe von Bereichen, in Zeilen von maximal 8 Bytes Länge zu zerlegen. Strikte tut es nur bei mit einer durch 8 teilbaren Adressen anfangen volle 8er Gruppen machen, bei anderem anfangen gibt es eine erste gekürzte Gruppe, dann volle. Auch das ist ein undokumentiertes Feature, weil keines der Beispiele im Handbuch es zeigt. Hier nachgeholt, für diese ganze Routine:

Benutzer:   FFC4.FFDB
Rechner:    FFC4: 86 2B A5 24
Rechner:    FFC8: C5 28 A5 25 E5 29 B0 C1
Rechner:    FFD0: E6 24 D0 02 E6 25 A5 24
Rechner:    FFD8: 29 07 10 C8
    

Dabei tut der BPL immer springen, weil 0..7 niemals negativ sind. Wieder in 2 Bytes, statt einem 3 Byte JMP welches immer springt. Bei $FFA4 sitzt dann der oben schon erwähnte Taktzyklen verschwendende BNE, der bei 0 durchfällt, und die Adresse wie gehabt ausgibt, bei 1..7 aber springt, und so die Adresse weglässt! Er hätte ohne weitere Bytes zu brauchen hier vor dem BPL liegen können, neben dort das Adresse Kommando beschleunigend (kein BNE), ebenfalls hier den häufigeren 7/8 Fall ohne weitere Adresse (nur BNE, kein BPL und dann BNE), mit dann langsamer BNE durchfallen und dann BPL (oder auch einen dann machbaren passenderen BEQ) nur in 1/8 der Fälle ablaufend. Und es wäre erst noch lesbarerer Code!

Das nächste Byte nach obigen BPL ist bereits die Adresse $FFDC vom PRBYTE. Also ist das Hauptprogramm hiermit durch! Es fehlen nur noch zwei ausgelassene kleine Sachen: Was geschieht wenn Hexzahlen im Modus nach ":" kommen, sowie was geschieht nach dem "R" Kommando?

Kommando Speichern

Zuerst kommt nach Hexzahl mit ":", um Daten/Programm zu speichern, wo wir nach dem gescheitertem BVC in $FF85 bei $FF87 weiter verfolgen ausgelassen hatten. Das ist nur noch sehr kurz:

       *** weiter in NOTHEX
FF87   A5 28      LDA L          nur untere Hälfte von 16bit Hexzahl
FF89   81 26      STA (STL,X)    Byte zu der Adresse (X = 0)
FF8B   E6 26      INC STL        nächste Adresse untere Hälfte
FF8D   D0 B5      BNE NEXTITEM   hole nächstes Zeichen
FF8F   E6 27      INC STH        nächste Adresse obere Hälfte

TONEXTITEM:
FF91   4C 44 FF   JMP NEXTITEM   hole nächstes Zeichen
    

Man beachte, dass ":" gar keine Addresse oder alten Inhalt ausgibt! Das macht lediglich die Adresse vor dem ":" setzen, welches diese bereits ausgegeben hat. Daher wird auch der alte Wert ausgegeben und nicht der neue! Das ":" macht einfach danach weiter, mit Daten von der Kommandozeile nehmen, ohne Ausgaben. Dies geht beliebig lange so, weil nach einem ":" kein neuer Hexzahlen Modus mehr gesetzt wird. Erst eine neue Kommandozeile kann diesen wieder zu 0 machen, und so eine neue Adresse eingeben erlauben, oder nach neuem ":" wieder nur Daten speichern.

Hier sieht man auch, dass dabei nur L als Daten benutzt wird, nicht H. So wird die Hexzahl von 4 Ziffern weiter auf 2 abgeschnitten. Genau daher kann man auch hier nach Fehlern in Daten tippen einfach 2 weitere Ziffern eingeben, und nur diese bleiben erhalten, die anderen 2 sind zwar weiterhin in H, aber haben keine Auswirkung!

Der STA mit STA (STL,X) ist selbiges Verfahren wie der LDA mit (XAML,X). Auch das INC Zeugs ist genaues Gegenstück. Der BNE und der JMP gehen beide zu NEXTITEM, um die Kommandozeile weiter abzuarbeiten. Genau dieser JMP bei TONEXTITEM ist auch wohin der obige zu weite BCS beim INC Zeugs hinspringt, der Mittelpunkt von dessen Zweisprung nach NEXTITEM.

Speziell ist nur, dass hier die separate Speicheradresse STL und STH benutzt wird, damit die Ausleseadresse in XAML und XAMH für "R" unverändert erhalten bleibt. Erst das erlaubt ein "R" direkt nach Daten eingeben, weil ja keine Adresse in dieser Daten Kommandozeile mehr folgen kann! Nur das brauchte die Kopie von H und L in XAMH und XAML sowie STH und STL, und somit die Kopierschleife dort.

Kommando Laufenlassen

Dann verbleibt nur noch nach dem "R" ein Programm laufen lassen, wo wir bei $FF94 weiter verfolgen weggelassen hatten. Das ist noch kürzer:

RUN:
FF94   6C 24 00   JMP (XAML)     Programm an Adresse laufen lassen
    

Auch dies ist ein Sprung. Aber wie die beiden LDA (XAML,X) und STA (STL,X), verwendet dieses (XAML) weil es in XAML und XAMH die reale Adresse des aufzurufenen Programmes erwartet. Aber X ist bei diesem Befehl nicht relevant. Ebenso ist dieser nicht auf die Adresse in der Zeropage sein limitiert, hat es 3 Bytes statt 2.

(Strikte ist dies gar kein JMP mit einem (XAML) Adressmodus, sondern ein JMP mit normalem XAML Adressmodus, gefolgt von einem zweiten impliziten JMP mit nur noch dessen Adressteil von XAML und XAMH holen. Was dann erst noch zu einem weiteren Prozessorbug geführt hat, der bei JMP ($xxFF) das erste Byte der indirekten Adresse von dort nimmt, aber das zweite in-Page von ($xx00), und nicht vom 00 der nächsten Page! Dieser Bug ist aber nur in der ursprünglichen NMOS 6502, er wurde dann in der CMOS 65C02 korrigiert.)

Wieder gibt "R" gar keine Addresse oder Daten aus. Lediglich die Adresse vor dem "R" machte dies, und "R" geht einfach danach weiter. Es werden auch keine Anstalten gemacht, dem aufgerufenen Program zu helfen. Weder wird die Bildausgabe mit einem Carriage Return aufgeräumt, das Programm muss sich selber Platz schaffen. Noch wird dem Program eine Rücksprungadresse für einen RTS hingelegt, geschweige denn ist der spezielle BRK Befehl als Abbruch benutzbar, wohl alles weil kein Platz mehr für etwas setzen oder BRK auswerten vorhanden war. Daher müssen Programme selber mit explizitem JMP GETLINE ($FF1F) in den Monitor zurückspringen.

Gesammtprogram

Oben hatte ich schon die ganzen 32*8 = 256 Bytes ohne Eingaben ich in einem separaten Hex Dump File im Apple 1 Ausgabeformat ausgegeben. Hier nun erweitert, halbwegs zwischen Hex und Assembler alles als symbolischen Dump File ohne Labels und Kommentare, in dem aber jedes Byte möglichst aussagekräftig interpretiert ist. Ideal ist beide Files neben einander vergleichen.

Darin sind Opcodes als 3 Buchstaben ausgeschrieben, Konstanten mit # plus 2 Hexziffern, Zeichen als #" plus das Zeichen, ein Carriage Return als #CR, Adressen als 2 revers angrordnete Bytes von je $ plus 2 Hexziffern, Sprungdistanzen mit < bei rückwärts und > bei vorwärts plus dann die hinteren/niederen 2 Hexziffern der neuen Adresse, indizierte nnnn,Y Adressierung als Ynn und $nn, indirekte (nn,X) Adressierung mit (Xnn, indirekte JMP (nnnn) Adressierung mit ($nn und $nn. So bekommt man eine erstaunlich gute Übersicht von wie es im PROM Speicher aussieht.

Programmkonventionen

Wie sehen nun die Programme aus, welche "R" aufrufen kann? Es wird lediglich im Handbuch, in der Monitor Anleitung, Seite 4, direkt nach dem "R" Kommando beschreiben, hingewiesen auf die drei Unterprogramme ECHO und PRHEX und PRBYTE, sowie empfohlen mit ersterem am Anfang ein CR auszugeben, sonst bleibt man stehen auf der Zeile mit dem ersten Byte ausgegeben.

Weil das Gegenstück zu ECHO ab NEXTCHAR fest eingebaut wurde, um JSR und RTS zu sparen, gibt es kein Unterprogramm dazu, muss jedes Programm welches die Tastatur lesen will diesen Treiber seine 8 Bytes in sich duplizieren. Was im Handbuch aber nicht steht, geschweige denn der Beispielcode dafür. Programme können aber auch die weitere Kommandozeile nach dem R abarbeiten, mit IN,Y. Was ebenfalls nicht im Handbuch steht, geschweige denn Beispielcode dafür hat.

Des weiteren wird noch hingewiesen, dass Programme mit JMP GETLINE zu beenden sind, um wieder sicher in den Monitor zu gelangen, sowie dass danach ein CR kommen wird. Oder man kann auch mit Reset beenden, was nach dem \ für Abbruch auch ein CR machen wird.

Mit all dies können wir nun obiges Demoprogrämmchen verstehen:

0300   A9 8D      LDA #$0D+$80   Carriage Return, neue Zeile, mit Bit7 = 1
0302   20 EF FF   JSR ECHO       Zeichen ausgeben
0305   A9 C1      LDA #"A"+$80   "A", mit Bit7 = 1
0307   20 EF FF   JSR ECHO       Zeichen ausgeben
030A   4C 1F FF   JMP GETLINE    beenden, zurück in Monitor
    

Die JSR ECHO sind die Systemaufrufe um je ein Zeichen ausgeben, wobei die LDA # davor die Zeichen vorgeben, und der JMP GETLINE ist Systemaufruf am Programmende, mit einem folgendem Return ausgeben. Wer ASCII kennt, sowie weiss dass der Apple 1 alle Zeichen mit Bit7=1 haben will, sieht bei den LDA #$8D und #$C1 dass es folglich die ASCII Zeichen 0D und 41 sind, also CR und "A". Womit dieses Demoprogrämmchen wie gesehen zuerst das obligate Return und A ausgibt, mit ersterem gleich nachdem der Rechner sein 0300: A9 ausgegeben hat, und dem GETLINE seinem Return danach.

Mit 6 weiteren LDA+JSR, mit den Konstanten D0 D0 CC C5 A0 B1, hätte es voll "APPLE 1" ausgeben können. Das kostet aber 5 Bytes pro Zeichen, und somit +6*5=30 Bytes, weshalb es sich schnell lohnt, diese kompakter mit einer Schleife zu machen, mit dann nur 1 Byte Daten pro Zeichen:

0300   A0 05      LDY #7         starte Schleife, für 8 Zeichen, 7..0
NEXTCHAR:
0302   B9 0E 03   LDA STRING,Y   Zeichen holen, von hinten(!) im String her
0305   20 EF FF   JSR ECHO       Zeichen ausgeben
0308   88         DEY            nächste Position in String
0309   10 F7      BPL NEXTCHAR     ist Y >= 0, noch Zeichen, weiter
030B   4C 1F FF   JMP GETLINE    beenden, zurück in Monitor
STRING:
030E   B1 A0 C5   .DB '1 SP 'E   CR und APPLE 1, reversiert, weil 7..0
0311   CC D0 D0   .DB 'L 'P 'P
0314   C1 8D      .DB 'A CR
    

Hinter dem Code in 0300..030D folgt der String in 030E..0315, mit so nur 14+8=22 Bytes statt obige 13 auf 43 anwachsend. Was bedeutet, dass ab 4 Zeichen mit 14+4=18 statt 4*5+1=21 sich diese Schleife lohnt, aber noch nicht bei 3 Zeichen mit 14+3=17 statt 3*5+1=16.

Ich verwende hier bewusst eine eigenartige Methode, mit den String reversiert im Speicher ablegen und von hinten her ausgeben, um diese zu zeigen! Das war früher eine normale Technik, um eine simple DEY Abzählschleife zu benutzen. Dies statt LDY #0, und nach einem INY mit einem zusätzlichen CMP #8 testen zu müssen, und danach BNE statt BPL benutzen. Oder statt am Ende vom String, welcher nur 7bit ASCII ist, das letzte Zeichen mit invertiertem Bit7 markieren, und nach dem LDA mit einem BPL die Schleife abbrechen, zu einem ORA #80 und zusätzlichen JSR ECHO vor dem JMP GETLINE, sowie nach einem INY ohne CMP stets springen mit BNE. Oder am besten am Anfang Y mit -8 laden, beim LDA dies mit STRING+8,Y kompensieren und dann INY benutzen gefolgt von BNE welches nach dem letzten INY abbricht, was genauso kompakt ist aber auch eigenartig.

Auch den String im Programcode so in 3-Byte .DB Portionen aufteilen war früher eine durchaus normale Technik, weil manche sehr einfachen Assembler maximal 3 Bytes pro Codezeile erzeugen konnten, auch bei Datenbytes.

Schlussbetrachtung

Damit sind wir die Hardware und den Monitor durch. Für sowenige Chips und soweinige Bytes an Code ist das ziemlich beeindruckend! Wer sich beschwert, dass der Apple 1 primitiv ist, er passt dafür in so wenig. Wer sich beschwert, dass das zu wenig sei, dem sei errinnert, dass im Vergleich dazu die Konkurrenz Altair 8800 oder Imsai 8080 gar keine Terminal Logik oder Bildschirm hatten, ebenso keine Tastatur, ebenso kein PROM, und so auch keinen Monitor oder Kommandozeile! Die waren nur Kisten mit an der Vorderseite Reihen von LEDs und Schaltern bzw Tastern, um den Speicher in Binär zu editieren, mit einem in Hardware(!) implementierten byteweisen Editor. Beim Imsai dies wenigstens mit breiten flachen fingerfreundlichen Schaltern, bei Altair nicht mal das.

Statt Hexzahlen tippen, war mit LEDs und Schaltern direkt in Binär anschauen bzw eingeben notwendig. Dazu pro Byte eingeben 8 Schalter kippen und einen "Speichern" Taster flippen, statt hier nur 2+1 Tasten tippen. Ebenso für Adresse 16 Schalter und einen "Adresse" Taster. Was stets nur ein Byte ausgab, weil kein .EndAdresse, und Anzeige auch nur 8 LEDs. Daher musste man für jedes weitere Byte einen "Adresse+" Taster benutzen, und den Speicher byteweise mit binär auf diesen 8 LEDs durchschreiten. Auch eingeben mit "Speichern" Taster ging den Speicher byteweise durch. Bis die Finger wehtaten, gerade auch beim Altair seinen stabförmigen Schaltern und Tastern. Mehr kann der Hardware Editor nicht. Das war weit langsamer und mühsamer zu benutzen.

Dazu kommt, dass obiges Demoprogrämmchen um A auszugeben zwar 1:1 in deren 8080 Prozessoren ihre Befehle übersetzbar ist, aber mangels Monitor scheitert. So hat der JMP am Ende kein Ziel, weil kein PROM um anzuspringen. Was man noch mit HALT Befehl benutzen substituieren kann. Im Altair oder Imsai steht der Prozessor nach dem Einschalten still, bis man ihn erst nach einem Program eingeben mit erneut Anfangsadresse Schaltern und "Start" Taster drücken laufen lässt! Ein HALT Befehl stoppt ihn wieder. Weit wichtiger aber haben die JSR keinen Systemaufruf um Zeichen auszugeben. Das ECHO mag zwar einfach sein, und die Befehle darin auch übersetzbar. Aber ohne eine PIA 6820 mit Terminal Logik dahinter, bzw wegen 8080 Prozessor wohl eher eine 8255 PIO, oder wegen altmodischer sein noch eher eine 8251 UART mit echtem seriellen Terminal dahinter, ist nichts zu machen. Damit verbleibt nur seine Daten im Speicher ablegen und mit Adresse eingeben auslesen, also äquivalent zu LDA #$ gefolgt von STA $ und HALT, mit dann dem Benutzer diese Addresse einschaltern und anzeigen. Somit ist bereits dieses kleine Progrämmchen dort ohne die Hardware zu erweitern praktisch unmachbar! Man merkt hier deutlich, wie sehr der Apple 1 ein massiver Fortschritt war.

Der Sol-20 hatte dagegen eine umsteckbare ROM/PROM/EPROM Karte mit einem Monitor drin, mit 1oder2kByte sogar 4bzw8 mal mehr als diese 256Byte. Darin bei 1k Karte ein minimaler Monitor namens CONSOL, bzw bei 2k Karte einen komfortableren namens SOLOS. Differenzen sind scheints erweiterte Kassettenband Software sowie erweiterbare/umschaltbare Treiber. Er hatte einen Videogenerator mit Bildschirm, sowie Tastatur bereits mitgeliefert, ebenso Netzteil und Gehäuse. Der Prozessor beschreibt den Bildspeicher direkt, er ist somit ein echter Homecomputer, trotz dass er eigentlich als Terminal designt und vermarktet wurde, aber im Wissen darum, dass er eigentlich ein voller Computer ist. Dies weil aus der VDM-1 Videokarte entstanden, welche anstelle eines Terminals benutzt, Altair oder Imsai gleich zu vollen Homecomputer-artigen aufrüsten konnte, strikte sogar zu vollen Personalcomputer-artigen, wenn man auch noch Floppys addierte. Aber er kam erst 2 Monate später und war doppelt so teuer (wenn auch mit Netzteil und Tastatur und Gehäuse im Preis enthalten). Damit war der Apple 1 ein recht guter Zwischenschritt, was genau ihn technologisch und historisch speziell macht.

(Der Sol-20 hatte von der VDM-1 her separaten 1k Bildspeicher (8* 91L02A SRAMs), voll ausgenutzt mit 64x16=1024 Zeichen, aus einem MCM6574 oder MCM6575 Font ROM, mit 128 Zeichensatz, 7x9 Font in 9x13 Feld und somit 576x204 Pixel. Das erlaubt volles ASCII 32..127, besser als Apple II, plus 32 Graphikzeichen (andere Auswahl je nach welchem der Font ROMs, aber beide ohne volle Blockgraphik). Es bleibt noch 1bit für normal oder cursor (= schaltbar revers/blinkend) Darstellung. Es hat auch keine Graphikmodi, sowie keinerlei Bitmap, ebenso keine Farben, damit unter Apple II. Dazu nur noch 1k Hauptspeicher (auch 8* 91L02A SRAMs), weil eben als Terminal designt. Tastatur erzeugt direkt ASCII. Verwendet einen 8080 Prozessor, mit S-100 Bus (wie Altair und IMSAI), aber diesen nur für die Slots. Er kann mit dem Bus aber auch beliebige DRAM oder SRAM Speicherkarten addieren bis 48k, sowie ROM Karten bis 12k, oder mehr RAM (wie bei der Language Card). Läuft mit separatem Videomonitor und Kassettenrekorder. Ist somit eher bereits mit einem Apple II vergleichbar, als mit Apple 1, trotz nur 2 Monate nach Apple I und fast 1 Jahr vor Apple II.)

Dazu ist der Apple 1 der Vorfahre vom massiv erweiterten Apple II. Zudem inspirierte er den PET 2001. Womit zwei der drei grossen 1977er Rechner, welche zusammen die Mikrocomputer Revolution lostraten, ideologisch vom Apple 1 abstammen. Damit war er ein bedeutender Entwicklungsschritt, was ihn auch historisch wichtig macht.

(Der dritte der drei grossen, der TRS-80, ist wiederum eine Kreuzung von massiv vereinfachtem Sol-20 mit etwas PET 2001 ihren Features. Mit vom 8080 abstammendem Z80 Prozessor. Mit Level I (Integer) Basic in 4k ROM, oder Level II (Microsoft mit Floating Point) Basic in 12kByte ROM. Ohne die 1k SRAM der Sol-20, weil als Rechner statt Terminal direkt mit 4..16k Speicher (4k mit 8* 4096 DRAMs oder 16k mit 8* 4116). Eine Erweiterungsbox kann weitere 32k Speicher addieren (2* 8* 16k), sowie Slots. Mit 64x16=1024 Zeichen Video (wie Sol-20) in separatem 1k*7(!)bit Bildspeicher (7* 2102 SRAMs), aber für Fernseher statt Monitor auch auf 32x16 (wie TV Typewriter) umschaltbar. Mit Font aus einem MCM6670 Font ROM, mit 64 Zeichensatz, 5x8 Font in 6x12 Feld und somit 384x192 Pixel. Mit nur ASCII 32..95 ohne Graphikzeichen, sowie ohne reverse oder blinkende Darstellung (wie Apple 1). Dies aber mit einem siebenten Bit erweitert, was erlaubt separate Hardware 2x3 Blockgraphik zu mischen für 128x48 Blöcke. Keine Graphikmodi (wie Sol-20 und PET 2001), kann Low Res aber teilweise mit der Blockgraphik kompensieren (wie PET 2001), hat ebenso keinerlei Bitmap, und keine Farben. Tastatur ist ohne ASCII per Spezialchip, mit Tastenmatrix direkt vom Prozessor einscannen (wie PET 2001). Läuft mit separatem Videomonitor und Kassettenrekorder, welche aber als Paket mitgeliefert wurden, alles in koordiniertem Design. Kostete trotzdem sogar nur $600 (strikte Rechner $400 + Monitor $200 + Kassettenrekorder $0 statt $50, weil im Paket als Dreingabe).)


Home | Informative Texts | Apple 1

Diese Seite ist von Neil Franklin, letzte Änderung 2024.01.19