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.
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.)
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.
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.
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.)
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.
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.)
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 nur die geraden benutzen wird bei allen 1970er/1980er Videomonitor basierten Terminals und Homecomputern und Konsolen benutzt. Es ergibt deren typisches horizontal streifiges Bild. Dies sollte bei gut gemachten Emulatoren auch so erscheinen, mit jede zweite Zeile schwarz ausgelassen. 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!)
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.)
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.
Benutzer: FF00Rechner: 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
Benutzer: FF00.FF1FRechner: 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 VCF Berlin 2019 Ausstellung 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.)
Benutzer: 300.30FRechner: 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.)
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.)
(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.)
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.407Rechner: 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.)
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. Deren Leute welche grösser haben wollten und deswegen geblockt wurden gründeten Zilog und brachten die stark 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 (oder gar nur 5zu10V?) Spannnungswandler, dann revidiert 5000 mit 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 mit 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, aber nur sofern der Speicher mithalten kann.
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).)
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.)
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!
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.)
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.)
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.
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.)
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 "?".)
*** 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.
*** 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.)
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 FFRechner: 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.
*** 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.)
*** 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.)
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?
*** 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.
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.
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.
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.
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).)
Diese Seite ist von Neil Franklin, letzte Änderung 2024.05.27