Ein Atmega328 steuert einen Z80-Computer

Themabewertung:
  • 2 Bewertung(en) - 5 im Durchschnitt
  • 1
  • 2
  • 3
  • 4
  • 5
Unilein
Fachgebiet Rauchentwicklung
*******

Beiträge: 728
Registriert seit: Apr 2014
Bewertung: 5
#71
02.01.2019, 20:08

Hallo zusammen,

allen noch ein frohes neues Jahr!

Und weil ich ein paar Tage frei habe, habe ich mich auch wieder mit meinem Projekt beschäftigt. Das im vorigen Beitrag erwähnte neue Design lasse ich erst mal ruhen. Jetzt stelle ich erst einmal das bestehende Design fertig.

Die IDE-Schnittstelle habe ich aufgebaut. Und wenn ich sie anschließe, wir sie sogar initialisiert. Ob sie aber wirklich funktioniert, habe ich noch nicht testen können. Ich muss das zugehörige Programm noch umschreiben.

Ich habe mich dann mal wieder auf mein Tastaturinterface fokussiert. Da habe ich ja bereits so einiges an Zeit investiert und bin regelmäßig gescheitert. Ich habe alle alten Ideen verworfen und habe mir überlegt, das Ganze in Arduino-manier umzusetzen. Für den Arduino gibt es die PS2Keyboard-Lib. Sie benötigt nicht sehr viel Speicher und kann mit verschiedenen Tastaturlayouts umgehen. Ich hatte mir überlegt, dafür den sowieso verbauten Atmega328 zu verwenden. Der hat noch Speicher frei. Allerdings benötigt die Lib einen echten Interrupt-Pin für das Clock-Signal der Tastatur. Beim Arduino Nano sind die Pins 2 und 3 für INT0 und INT1 zuständig. Leider sind diese beiden Pins aber auch Bestandteil des Ports D, über den der Atmega den RAM-Baustein des Z80Ardu mit dem Programm versorgt. Und leider würde ich diesen Port auch für die Übertragung der Tastaturdaten benötigen. Das funktioniert also nicht.

Am Z80Ardu hätte ich das Platinendesign ein wenig anpassen müssen. Ein paar Steuerleitungen hätten anders angeschlossen werden müssen. Auf dem Board sind noch Gatter frei, die ich dann für die Steurung hätte verwenden können.... Aber geht halt nicht...

Also neue Idee entwickeln.

Die neue Idee sieht wie folgt aus. Sie ist leider etwas komplizierter. Zum Einsatz kommt ein Atmega 168 oder Atmega 8 oder Atmega 328. Die sind alle Pinkompatibel; mal sehen, welcher sich vom Speicher her am besten eignet. Man könnte also sagen Arduino Nano oder Uno.
Da auch hier Port D verwendet werden müsste um die Daten parallel an den Z80Ardu zu übertragen, ich aber einen der beiden Interrupt-Pins von dem Port benötige, kann also keine parallele Datenübertragung erfolgen. Stattdessen müssen die Daten seriell bereit gestellt werden. Dazu käme dann ein SN74HC595 Schieberegister zum Einsatz. Dieser stellt die seriell zugeführten Daten parallel zur Verfügung. An den Ausgängen des Schieberegisters wir ein SN74HCT573 Latch nachgeschaltet. Dieser nimmt die Daten vom Schieberegister an und gibt diese an den Datenbus des Z80Ardu weiter.

Um auf dem Z0Ardu nicht unnötig Rechenleistung zu verschwenden, würde das Tastaturinterface auf dem Z80Ardu einen NMI (Non Maskable Interrupt) auslösen. Der Z80Ardu würde daraufhin eine Datenanfrage an das Interface schicken. Das Latch würde die Daten dann auf den Datenbus legen.

Soweit mal zur Theorie.... Bislang noch nicht aufgebaut. Lediglich ein kleines Testprogramm mit der PS2Keyboard-Lib habe ich probiert. Dieses gibt die auf der Tastatur eingegebenen Buchstaben über die serielle Schnittstelle aus. Das funktioniert einwandfrei.

Falls jemand noch eine andere Idee hat, dann gerne her damit. Das ist nämlich so ziemlich viel Aufwand für so ein einfaches Interface.

LG
Uni
Zitieren
Unilein
Fachgebiet Rauchentwicklung
*******

Beiträge: 728
Registriert seit: Apr 2014
Bewertung: 5
#72
03.01.2019, 15:06

Zitat:Da auch hier Port D verwendet werden müsste um die Daten parallel an den Z80Ardu zu übertragen, ich aber einen der beiden Interrupt-Pins von dem Port benötige, kann also keine parallele Datenübertragung erfolgen. Stattdessen müssen die Daten seriell bereit gestellt werden. Dazu käme dann ein SN74HC595 Schieberegister zum Einsatz. Dieser stellt die seriell zugeführten Daten parallel zur Verfügung. An den Ausgängen des Schieberegisters wir ein SN74HCT573 Latch nachgeschaltet. Dieser nimmt die Daten vom Schieberegister an und gibt diese an den Datenbus des Z80Ardu weiter.

Ich werde das heute mal aufbauen und probieren. Mir ist bei der Durchsicht des Datenblatts zum 74HC595 aufgefallen, dass dieser auch einen /OE-Pin (Output Enable) hat. Wenn ich das richtig interpretiere, könnte ich auf das Latch verzichten. Ein Bauteil weniger. Muss ich aber erst einmal überprüfen.

LG
Uni
Zitieren
Unilein
Fachgebiet Rauchentwicklung
*******

Beiträge: 728
Registriert seit: Apr 2014
Bewertung: 5
#73
03.01.2019, 18:43

Hallo zusammen,

ich habe die Theorie heute mal in die Praxis umgesetzt. Zunächst lediglich mit dem Atmega328P (Arduino Nano) und dem Schieberegister 74HC595. Den Schaltplan habe ich unten angehängt. Natürlich baue ich das Ganze wieder mit "nackten Chips" und nicht mit dem Arduino, da ich das Ganze ja in meinen Z80Ardu integrieren möchte.

Ich habe im ersten Schritt auf das Latch verzichtet und es sieht so aus, als würde das auch gehen. Zumindest mit LED's an den Ausgängen des Schieberegisters passt es. Da es sich beim 74HC595 um die CMOS-Variante des IC's handelt, könnte es sein, dass die Signalpegel nicht zum Z80Ardu passen, da die CMOS-Pegel von TTL-Pegel abweichen. Die Lösung wäre ein 74HC T 595. Der liefert TTL-Pegel oder alternativ einen Pegelwandler. Aber ich probiere es zunächst mal ohne.

Hier ist der Code, der sich auf dem Atmega befindet. Entwickelt in der Arduino IDE, würde also auch mit einem Nano funktionieren. Der Code ist sogar so kompakt, dass es auch mit einem Atmega 8 oder Atmega 168 funktionieren würde.

Code:
/* Z80Ardu Keyboard Interface
    based on AtMega328P and Shiftregister SN74HC595


   Funktionsweise:

   In der Hauptschleife wird die Tastatur abgefragt. Sobald eine Taste gedrückt wird,
   wird der Tastencode per shiftOut() an den SN74HC595 übergeben (8 Bit)

   Sobald der shiftOut erledigt ist, wird über Pin xxx ein NMI (Non Maskabe Interrupt)
   an der Z80 CPU ausgelöst. Die CPU führt daraufhin den an Adresse 0066h befindlichen Code aus
   (Z80-Standard)

   In der NMI-Routine wird dann das Signal /OE des Schieberegister auf LOW gelegt, wodurch
   die im Schieberegister befindlichen Daten auf die Ausgänge und somit auf den Datenbus
   des Z80Ardu gelegt werden (Z80-Befehl: in a,(20h) ).

   Sobald /OE wieder auf HIGH ist, ist der Vorgang abgeschlossen. Das Schieberegister wird
   deaktiviert (/OE = HIGH)

*/


#include <PS2Keyboard.h>

// Pins für die Tastaturabfrage
const int KeybDataPin = 8;
const int KeybIRQpin =  2;


// Pins für das Schieberegister
// Pin verbunden mit dem latch pin (ST_CP) des 74HC595
const int SRlatchPin = 7;
// Pin verbunden mit clock pin (SH_CP) des 74HC595
const int SRclockPin = 6;
// Pin verbunden mit Data in (DS) des 74HC595
const int SRdataPin = 5;

// Pin für NMI
const int NMI = 9;   // PB1

PS2Keyboard keyboard;

void setup() {
  // Wir verwenden das deutsche Tastaturlayout
  keyboard.begin(KeybDataPin, KeybIRQpin, PS2Keymap_German);

  // Schieberegister-Steuerpins auf Ausgabe
  pinMode(SRlatchPin, OUTPUT);
  pinMode(SRdataPin, OUTPUT);
  pinMode(SRclockPin, OUTPUT);

  // NMI-Pin auf Ausgabe
  pinMode(NMI, OUTPUT);
  digitalWrite(NMI, HIGH);    // NMI auf High --> Kein Interrupt

  //Schieberegister auf 0
  digitalWrite(SRlatchPin, LOW);
  shiftOut(SRdataPin, SRclockPin, MSBFIRST, 0);
  digitalWrite(SRlatchPin, HIGH);

}

void loop() {
  // In der Schleife die Tastatur abfragen
  // Wenn eine Taste auf der Tastatur gedrückt wird, wird der Tastatencode
  // nach "c" übertragen. Von dort aus über das Schieberegister auf den Datenbus
  if (keyboard.available()) {
    char c = keyboard.read();     // Tastencode nach c

    // Wenn der Tastencode <> 0 ist, dann Daten ins Schieberegister
    // und NMI auslösen
    if (c != 0) {
      // Tastencode ins Schieberegister
      digitalWrite(SRlatchPin, LOW);
      shiftOut(SRdataPin, SRclockPin, MSBFIRST, c);
      digitalWrite(SRlatchPin, HIGH);

      // NMI auslösen
      digitalWrite(NMI, LOW);
      digitalWrite(NMI, HIGH);
    }
  }
}


und hier ist der Schaltplan:


   

Grüße
Uni
(Dieser Beitrag wurde zuletzt bearbeitet: 03.01.2019, 19:10 von Unilein.)
Zitieren
Unilein
Fachgebiet Rauchentwicklung
*******

Beiträge: 728
Registriert seit: Apr 2014
Bewertung: 5
#74
05.01.2019, 17:02

Läuft!!

Heute habe ich es endlich geschafft. Eine PS/2-Tastatur arbeitet mit dem Z80Ardu zusammen. Wie zuvor beschrieben unter Zuhilfenahme des Atmega328 und eines Schieberegisters.

Der reine Test mit den LED's als Datenanzeige hat gestern schon funktioniert. Als ich heute mit dem Z80Ardu getestet habe, musste ich zwei Delays ins Arduinoprogramm einbauen (zumindest vermutet ich, dass das nötig war). Außerdem ist die Spannungsversorgung des Z80Ardu zu schwach, um die Tastatur adäquat zu versorgen. Mit einer externen Spannungsversorgung und gemeinsamer Masse ging es dann.

Ich werde die Konstruktion mal auf eine kleine Platine löten um das Z80-Programm entsprechend entwickeln zu können. Denn mit einer Tastatur wird einiges einfacher! Wenn das so funktioniert, werde ich wieder passende Platinen entwerfen und bestellen. Allerdings werde ich den Platz, der dort noch zur Verfügung steht, für weitere Dinge nutzen. Zum Beispiel für den Soundchip und evtl. auch für die serielle Schnittstelle.

Das Ergebnis habe ich Euch als Youtube-Video eingefügt. (Bitte entschuldigt das verwackelte Bild, tippen mit einer Hand und gleichzeitig mit dem Handy filmen, sowie jeden Buchstaben mit SHIFT eingeben, ist nicht so einfach Wink )






Grüße
Uni
[Bild: 2d339f25ac694488b70a52741039baf5.jpg]
-----

Klopapier beidseitig verwenden und der Erfolg liegt auf der Hand!
(Dieser Beitrag wurde zuletzt bearbeitet: 05.01.2019, 17:09 von Unilein.)
Zitieren
Unilein
Fachgebiet Rauchentwicklung
*******

Beiträge: 728
Registriert seit: Apr 2014
Bewertung: 5
#75
05.01.2019, 20:03

Hallo zusammen,

und weil's auf dem Breadboard funktioniert hat, habe ich das gleich mal als Prototyp auf eine Platine gelötet:


[Bild: 4c5d999ad31f4c2d53d681c09620a006.jpg]


Jetzt muss ich noch die Scancodes im Keyboardprogramm auf dem AtMega anpassen, da der VDG 6847 keinen echten ASCII-Code kennt. Ich lege die Codes dort quasi mundgerecht ab, denn dann muss ich das auf dem Z80 nicht programmieren. Das spart jede Menge Speicherplatz.

Das Programm auf dem AtMega ist übrigens 2350 Byte groß... Da könnte man also noch jede Menge Kram rein packen.

Gruß
Uni
-----

Klopapier beidseitig verwenden und der Erfolg liegt auf der Hand!
(Dieser Beitrag wurde zuletzt bearbeitet: 05.01.2019, 20:10 von Unilein.)
Zitieren
Titanes
Member
***

Beiträge: 69
Registriert seit: Aug 2015
Bewertung: 0
#76
06.01.2019, 20:18

Hallo,

ich finde es immer toll Deine Fortschritte zu sehen. Du mußt echt eine Engelsgeduld und viel Wissen haben. Bin schon gespannt wie es weiter geht.

Gruß

Titanes

Gesendet von meinem SM-T800 mit Tapatalk
Zitieren
Unilein
Fachgebiet Rauchentwicklung
*******

Beiträge: 728
Registriert seit: Apr 2014
Bewertung: 5
#77
06.01.2019, 21:54

Zitat:Du mußt echt eine Engelsgeduld und viel Wissen haben. Bin schon gespannt wie es weiter geht.

Geduld... ähm... Sagen wir mal so: zwischendurch muss ich hin und wieder durchatmen, weil's einfach nicht klappt. An dem drecks Tastaturding habe ich lange geknabbert. Und auch das Zusammenspiel zwischen Prozessorboard und Grafikkarte war echt nervenzermürbend.

Was mein Wissen anbelangt: Es wird natürlich mehr. Aber ich hatte es ja schon einmal erwähnt, vor ca 4 Jahren hatte ich davon überhaupt noch keine Ahnung.

Was die weiteren Schritte im Projekt angeht, werde ich zunächst einmal die Scancodes für die Tastatur soweit klar machen, dass ich alle Zeichen des MC6847 darstellen kann. Der Chip kann nur Großbuchstaben (standardmäßig), diese aber in normal und invertiert. Außerdem gibt es einen Satz "Grafikzeichen" bei dem die Zeichen jeweils aus 4 kleinen Blöcken bestehen. Der Hintergrund ist dabei immer schwarz, der sichtbare Teil der Blockgrafik kann je eine von 8 Farben annehmen.

Diese Zeichen müssen auf jeden Fall alle über die Tastatur erreichbar sein.

Wenn das dann passt, werde ich eine kleine Routine für den Z80 schreiben, mit der ich Daten auf dem Schirm ausgeben kann. Dabei soll berücksichtigt werden, dass am Ende des Bildschirms der Inhalt um eine Zeile nach ober geschoben werden muss.

Eine weitere kleine Routine nimmt Tastatureingaben an und reagiert entsprechend. Das dürfte eine Kleinigkeit sein, denn im ersten Schritt werde ich nur einzelne Tasten abfragen.

Tja... Und dann kommt die IDE-Schnittstelle. Ich habe bereits ein kleines Programm, welches Daten über die IDE-Schnittstlele lädt. Dafür benötige ich aber die Bildschirmausgabe und die Tastatureingabe. Nur damit kann ich das Programm bedienen... Ansonsten würde das über eine serielle Schnittstelle laufen. Die habe ich aber momentan nicht parat.

Und dann werde ich weiter am BASIC-Interpreter arbeiten und dort die IDE-Schnittstelle "einflechten"...

...noch ein langer weg dorthin.

Gruß,
Uni
Zitieren
Unilein
Fachgebiet Rauchentwicklung
*******

Beiträge: 728
Registriert seit: Apr 2014
Bewertung: 5
#78
31.01.2019, 16:14

Hallo zusammen,

hab mich ja schon wieder ein Weilchen nicht mehr zum Projekt geäußert. Also wird es wieder Zeit für ein kurzes Update.
Aktuell findet keine Hardware-Entwicklung statt. Der Rechner funktioniert, die Grafik klappt, die Tastatur tut was sie soll.
Die IDE-Schnittstelle liegt fertig aufgebaut auf meinem Basteltisch.

Jetzt muss ich, um einen sauberen Test der IDE-Schnittstelle fahren zu können, ein Testprogramm entwickeln, welches die Funktionen
der IDE -Schnittstelle aufruft und dann Feedback auf dem Bildschirm gibt. Ich habe also damit angefangen, Bildschirm- und Tastaturroutinen
in Maschinensprache zu entwickeln, die mir helfen sollen, besser testen zu können.

Aktuell arbeite ich primär an der Bildschirmausgabe (Einzelne Zeichen ausgeben, Zeichenketten ausgeben, Bildschirmscrolling usw). Die
Routinen stehen soweit und sind aus meiner Sicht geeignet, entsprechende Bildschirmausgaben zu generieren.

Die Routine habe ich einfach mal eingefügt. Der interessierte Leser kann ja mal rein schnuppern. Ein paar Kleinigkeiten sind mir im Nachhinein
noch eingefallen, es handelt sich dabei aber um reine Kosmetik bei der Bildschirmausgabe.

Wichtig ist, dass der Code so, wie er dargestellt ist, prinzipiell individuell einsetzbar ist (auf einem Z80-Prozessor mit einer Grafikeinheit
mit Video-RAM, welches im 64K Adressraum gemapped ist). Der genutzte MC6847 stellt allerdings keine ASCII-Codes dar. Das "A" ist im ASCII-Code
mit der Nummer 65 versehen. Auf dem MC6847 ist der zugehörige Code "1". Da muss also ein wenig umgerechnet werden.

Gruß
Uni

 
vidram:             equ 0e000h
vidram_hi:          equ HI vidram + 2
vidram_sline:       equ vidram + 020h
vidram_n2last:      equ vidram + 1e0h
 
 
                   ld a,020h
                   call _cls
 
                   ; Einzelnes Zeichen ausgeben
                   ld b, 0                  ; X
                   ld c, 1                  ; Y
                   call calc_pos            ; Position am Bildschirm berechnen
                   ld a,"A"                 ; Zeichen zur Ausgabe
                   call putc
                   
                   ; Zeichenkette ausgeben
                   ld hl,stradr
                   call puts_calc
                   ld hl,stradr2
                   call puts_calc
self:               jp self
 
 
 
stradr:             defm 00h,0fh
                   defm "ABCEDFGHIJKLMNOPQRSTUVWXYZ      "
                   defm ".........   01234567891234567890",0,0
 
stradr2:            defm 00h,00h
                   defm "DIES IST EIN TEST!",0
 
; -------------------------------------------------------------------------
 
 
puts_calc:          ld a,(hl)                ; X-Position in A
                   ld b,a                   ; und nach B
                   inc hl                   ; Nächste Adresse
                   ld a,(hl)                ; Y-Position in A
                   ld c,a                   ; und nach C
                   call calc_pos            ; Position auf dem Schirm kalkulieren
_nextchar:          inc hl                   ; HL zegt auf den Text
_puts:              ld a,(hl)                ; Ein Zeichen des Strings lesen
                   cp 00h
                   jr z, endputs            ; Stringabschluss gefunden
 
                   call putc                ; Zeichen ausgeben
                   jr _nextchar             ; Nächstes Zeichen im String
endputs:            ret                    
 
 
; Ein Zeichen auf dem Bildschirm ausgeben (Ohne Positionsangabe)
putc:               push hl                 ; Alle Register retten
                   push de
                   push bc
 
_putc2:             ld hl,(_curpos)         ; Neue Cursorposition laden
 
                   ; Zeichen an MC6847-Zeichentabelle anpassen und Sonder-
                   ; funktionen ausführen
 
 
_noblockx:          cp 20h
                   jr z, _stchr
 
                   cp "a"
                   jr nc, _stchr
 
                   res 6,a
 
 
_stchr:             ld (hl),a
                   
                   inc hl
                   ld (_curpos),hl
               
                   ; Prüfen ob letzte Zeile erreicht
                   push af
                   ld a,h                    ; Highbyte der aktuellen Position nach a
                   cp vidram_hi
                   jr nz, end_putc
               
                   ; Bildschirm scrollen
                   ld hl, vidram_sline
                   ld de, vidram
                   ld bc, 0200h -20h
                   ldir
 
                   ; Letzte Zeile leeren
                   ld b, 020h                ; Zeilenlänge 32 Zeichen
                   ld hl,vidram_n2last
                   ld a, 020h                ; Füllzeichen = Leerzeichen
_clrline:           ld (hl),a
                   inc hl
                   djnz _clrline
 
                   ; Cursor-zeiger auf Anfang der letzten Zeile
                   ld hl, vidram_n2last
                   ld (_curpos),hl
 
end_putc:           pop af
                   pop bc
                   pop de
                   pop hl
                   ret
 
         
; Cursorposition berechnen aus X (0 - 31)
;                          und Y (0 - 15)
; X-Position nach b
; Y-Position nach c
; HL enthält die Bildschirmposition als 16-Bit
; adresse
calc_pos:           push hl
                   push bc             ; X und Y auf den Stack
                   ld b, 08h
                   ld d, 00h
                   ld e, 20h
                   ld hl, 0000h
         
loop:               add hl,hl
                   sla c
                   jr nc, zero
                   add hl,de
zero:               djnz loop
         
                   pop bc              ; X-Wert vom Stack holen
                   ld c,b
                   ld b,00h  
                   add hl,bc
                   ld bc, vidram
                   add hl,bc
                   ld (_curpos),hl
                   pop hl
                   ret                                  
 
                  ; *********************************************
                  ; Bildschirm löschen (volle 6 KB)
                  ; *********************************************
_cls:              push hl                                                 ; HL auf den Stack
                  push de                                                 ; DE auf den Stack
                  push bc                                                 ; bc auf den Stack
                  ld hl,vidram    
                  ld b,0                                                  ; 256 * 24 Bytes = 6144 Bytes
                  ld d,24                  
_clearit:          ld (hl),a                                               ; den Inhalt von A in den Speicher prügeln
                  inc hl
                  djnz _clearit
                  dec d
                  jp nz,_clearit
                  pop bc                                                  ; BC vom Stack holen
                  pop de                                                  ; DE vom Stack holen
                  pop hl                                                  ; HL vom Stack holen
                  ret
 
 
 
_curpos:            defw vidram                                        
_txtcolor:          defb 03h
 
 
(Dieser Beitrag wurde zuletzt bearbeitet: 31.01.2019, 16:20 von Unilein.)
Zitieren
Unilein
Fachgebiet Rauchentwicklung
*******

Beiträge: 728
Registriert seit: Apr 2014
Bewertung: 5
#79
05.02.2019, 14:06

Hallo zusammen,

die Ausgaberoutine habe ich noch einmal überarbeitet. Jetzt kann sie auch Zeilenschaltungen und vollständiges Bildschirmscrolling, wenn der Bildschirm voll ist.

 
vidram:                 equ 0E000h
vidram_hi:              equ HI vidram + 2
vidram_sline:           equ vidram + 020h
vidram_n2last:          equ vidram + 1e0h
 
                       
                       ld a,020h
                       call _cls
 
                       ; Einzelnes Zeichen ausgeben
                       ;ld b, 0                        ; X
                       ;ld c, 0                        ; Y
                       ;call calc_pos        ; Position am Bildschirm berechnen
                       ;ld a,"#"                       ; Zeichen zur Ausgabe
                       ;call putc
                       
                       ; Zeichenkette ausgeben
kette:                  ld hl,stradr
                       call puts_calc
self:                   jp self
 
 
 
stradr:                 defm 00h,03h
                       defm "-------------------------------",0dh
                       defm "Z80ARDU",0dh,"A Z80-PROJECT WITH",0dh
                       defm "ATMEGA328, SD-CARD, MULTI-IO",0dh,0dh
                       defm 0dh,"© 2016-19 BY MDH (UNILEIN)",0dH
                       defm "-------------------------------",0dh
                       defm 0dh,0dh
                       defm "GREETINGS TO MICRO-DEV.DE",0dh,0dh,0
 
 
; -------------------------------------------------------------------------
 
 
puts_calc:              ld a,(hl)                      ; X-Position in A
                       ld (var_curposx),a
                       ld b,a                         ; und nach B
                       inc hl                         ; Nächste Adresse
                       ld a,(hl)                      ; Y-Position in A
                       ld (var_curposy),a
                       ld c,a                         ; und nach C
                       call calc_pos                  ; Position auf dem Schirm kalkulieren
_nextchar:              inc hl                         ; HL zegt auf den Text
_puts:                  ld a,(hl)                      ; Ein Zeichen des Strings lesen
                       cp 00h
                       jr z, endputs                  ; Stringabschluss gefunden
 
 
                       call putc                      ; Zeichen ausgeben
                       jr _nextchar                   ; Nächstes Zeichen im String
endputs:                ret                
 
 
; Ein Zeichen auf dem Bildschirm ausgeben (Ohne Positionsangabe)
putc:                   push hl                        ; Alle Register retten
                       push de
                       push bc
 
                       ; Zeichen an MC6847-Zeichentabelle anpassen und Sonder-
                       ; funktionen ausführen
 
                       cp 20h                         ; Leerzeichen?
                       jr z, _stchr                   ; Ja, dann Bit 6 lassen wie es ist
                       
                       cp 0dh
                       jr z, _putc_cr
 
                       res 6,a                        ; Bit 6 löschen = Code - 64
 
 
_stchr:                 ld hl,(var_curadr)             ; Neue Cursorposition laden
                       ld (hl),a
                       
 
                       inc hl
                       ld (var_curadr),hl
                   
                       ; Prüfen ob letzte Zeile erreicht
_lastline:              push af
                       ld a,h                         ; Highbyte der aktuellen Position nach a
                       cp vidram_hi
                       jr nz, end_putc
                   
                       ; Bildschirm scrollen
                       ld hl, vidram_sline
                       ld de, vidram
                       ld bc, 0200h -20h
                       ldir
 
                       ; Letzte Zeile leeren
                       ld b, 020h                     ; Zeilenlänge 32 Zeichen
                       ld hl,vidram_n2last
                       ld a, 020h                     ; Füllzeichen = Leerzeichen
_clrline:               ld (hl),a
                       inc hl
                       djnz _clrline
 
                       ; Cursor-zeiger auf Anfang der letzten Zeile
                       ld hl, vidram_n2last
                       ld (var_curadr),hl
 
end_putc:               pop af
                       pop bc
                       pop de
                       pop hl
                       ret
 
_putc_cr:               ld a,00h                       ; X=0
                       ld (var_curposx),a             ; Speichern
                       ld a,(var_curposy)             ; aktuelle Y-Position nach a
                       inc a                          ; Y in C und +1
                       ld (var_curposy),a             ; Speichern
                       ld bc,(var_curpos)
                       call calc_pos                  ; Bildschirnadresse errechnen
                       jr _lastline
 
 
 
; *********************************************            
; Cursorposition berechnen aus X (0 - 31)
;                          und Y (0 - 15)
; X-Position nach b
; Y-Position nach c
; HL enthält die Bildschirmposition als 16-Bit
; adresse
; *********************************************
calc_pos:               push hl
                       push bc                        ; X und Y auf den Stack
                       ld b, 08h
                       ld d, 00h
                       ld e, 20h
                       ld hl, 0000h
           
loop:                   add hl,hl
                       sla c
                       jr nc, zero
                       add hl,de
zero:                   djnz loop
           
                       pop bc                         ; X-Wert vom Stack holen
                       ld c,b
                       ld b,00h  
                       add hl,bc
                       ld bc, vidram
                       add hl,bc
                       ld (var_curadr),hl
                       pop hl
                       ret                                  
 
 
; *********************************************
; ** Bildschirm löschen (volle 6 KB)
; *********************************************
_cls:                   push hl                        ; HL auf den Stack
                       push de                        ; DE auf den Stack
                       push bc                        ; bc auf den Stack
                       ld hl,vidram    
                       ld b,0                         ; 256 * 24 Bytes = 6144 Bytes
                       ld d,24                        
_clearit:               ld (hl),a                      ; den Inhalt von A in den Speicher prügeln
                       inc hl
                       djnz _clearit
                       dec d
                       jp nz,_clearit
                       pop bc                         ; BC vom Stack holen
                       pop de                         ; DE vom Stack holen
                       pop hl                         ; HL vom Stack holen
                       ret
 
 
 
var_curadr:             defw vidram                    ; Aktuelle Cursoradresse 16 Bit
var_curpos:
var_curposy:            defb 00h                       ; Cursorspalte (Y)
var_curposx:            defb 00h                       ; Cursorzeile  (X)
var_txtcolor:           defb 00h
 

Auf dem Bildschirm sieht das dann so aus (Ich habe das Programm auf dem VZEMU-Emulator getestet. Der emuliert den MC6847 und ist auch sonst dem Z80Ardu nicht unähnlich):

   

Gruß
Uni
(Dieser Beitrag wurde zuletzt bearbeitet: 05.02.2019, 14:07 von Unilein.)
Zitieren
Titanes
Member
***

Beiträge: 69
Registriert seit: Aug 2015
Bewertung: 0
#80
06.02.2019, 23:15

(05.02.2019, 14:06)Unilein schrieb: Hallo zusammen,

die Ausgaberoutine habe ich noch einmal überarbeitet. Jetzt kann sie auch Zeilenschaltungen und vollständiges Bildschirmscrolling, wenn der Bildschirm voll ist.

 
vidram:                 equ 0E000h
vidram_hi:              equ HI vidram + 2
vidram_sline:           equ vidram + 020h
vidram_n2last:          equ vidram + 1e0h
 
                       
                       ld a,020h
                       call _cls
 
                       ; Einzelnes Zeichen ausgeben
                       ;ld b, 0                        ; X
                       ;ld c, 0                        ; Y
                       ;call calc_pos        ; Position am Bildschirm berechnen
                       ;ld a,"#"                       ; Zeichen zur Ausgabe
                       ;call putc
                       
                       ; Zeichenkette ausgeben
kette:                  ld hl,stradr
                       call puts_calc
self:                   jp self
 
 
 
stradr:                 defm 00h,03h
                       defm "-------------------------------",0dh
                       defm "Z80ARDU",0dh,"A Z80-PROJECT WITH",0dh
                       defm "ATMEGA328, SD-CARD, MULTI-IO",0dh,0dh
                       defm 0dh,"© 2016-19 BY MDH (UNILEIN)",0dH
                       defm "-------------------------------",0dh
                       defm 0dh,0dh
                       defm "GREETINGS TO MICRO-DEV.DE",0dh,0dh,0
 
 
; -------------------------------------------------------------------------
 
 
puts_calc:              ld a,(hl)                      ; X-Position in A
                       ld (var_curposx),a
                       ld b,a                         ; und nach B
                       inc hl                         ; Nächste Adresse
                       ld a,(hl)                      ; Y-Position in A
                       ld (var_curposy),a
                       ld c,a                         ; und nach C
                       call calc_pos                  ; Position auf dem Schirm kalkulieren
_nextchar:              inc hl                         ; HL zegt auf den Text
_puts:                  ld a,(hl)                      ; Ein Zeichen des Strings lesen
                       cp 00h
                       jr z, endputs                  ; Stringabschluss gefunden
 
 
                       call putc                      ; Zeichen ausgeben
                       jr _nextchar                   ; Nächstes Zeichen im String
endputs:                ret                
 
 
; Ein Zeichen auf dem Bildschirm ausgeben (Ohne Positionsangabe)
putc:                   push hl                        ; Alle Register retten
                       push de
                       push bc
 
                       ; Zeichen an MC6847-Zeichentabelle anpassen und Sonder-
                       ; funktionen ausführen
 
                       cp 20h                         ; Leerzeichen?
                       jr z, _stchr                   ; Ja, dann Bit 6 lassen wie es ist
                       
                       cp 0dh
                       jr z, _putc_cr
 
                       res 6,a                        ; Bit 6 löschen = Code - 64
 
 
_stchr:                 ld hl,(var_curadr)             ; Neue Cursorposition laden
                       ld (hl),a
                       
 
                       inc hl
                       ld (var_curadr),hl
                   
                       ; Prüfen ob letzte Zeile erreicht
_lastline:              push af
                       ld a,h                         ; Highbyte der aktuellen Position nach a
                       cp vidram_hi
                       jr nz, end_putc
                   
                       ; Bildschirm scrollen
                       ld hl, vidram_sline
                       ld de, vidram
                       ld bc, 0200h -20h
                       ldir
 
                       ; Letzte Zeile leeren
                       ld b, 020h                     ; Zeilenlänge 32 Zeichen
                       ld hl,vidram_n2last
                       ld a, 020h                     ; Füllzeichen = Leerzeichen
_clrline:               ld (hl),a
                       inc hl
                       djnz _clrline
 
                       ; Cursor-zeiger auf Anfang der letzten Zeile
                       ld hl, vidram_n2last
                       ld (var_curadr),hl
 
end_putc:               pop af
                       pop bc
                       pop de
                       pop hl
                       ret
 
_putc_cr:               ld a,00h                       ; X=0
                       ld (var_curposx),a             ; Speichern
                       ld a,(var_curposy)             ; aktuelle Y-Position nach a
                       inc a                          ; Y in C und +1
                       ld (var_curposy),a             ; Speichern
                       ld bc,(var_curpos)
                       call calc_pos                  ; Bildschirnadresse errechnen
                       jr _lastline
 
 
 
; *********************************************            
; Cursorposition berechnen aus X (0 - 31)
;                          und Y (0 - 15)
; X-Position nach b
; Y-Position nach c
; HL enthält die Bildschirmposition als 16-Bit
; adresse
; *********************************************
calc_pos:               push hl
                       push bc                        ; X und Y auf den Stack
                       ld b, 08h
                       ld d, 00h
                       ld e, 20h
                       ld hl, 0000h
           
loop:                   add hl,hl
                       sla c
                       jr nc, zero
                       add hl,de
zero:                   djnz loop
           
                       pop bc                         ; X-Wert vom Stack holen
                       ld c,b
                       ld b,00h  
                       add hl,bc
                       ld bc, vidram
                       add hl,bc
                       ld (var_curadr),hl
                       pop hl
                       ret                                  
 
 
; *********************************************
; ** Bildschirm löschen (volle 6 KB)
; *********************************************
_cls:                   push hl                        ; HL auf den Stack
                       push de                        ; DE auf den Stack
                       push bc                        ; bc auf den Stack
                       ld hl,vidram    
                       ld b,0                         ; 256 * 24 Bytes = 6144 Bytes
                       ld d,24                        
_clearit:               ld (hl),a                      ; den Inhalt von A in den Speicher prügeln
                       inc hl
                       djnz _clearit
                       dec d
                       jp nz,_clearit
                       pop bc                         ; BC vom Stack holen
                       pop de                         ; DE vom Stack holen
                       pop hl                         ; HL vom Stack holen
                       ret
 
 
 
var_curadr:             defw vidram                    ; Aktuelle Cursoradresse 16 Bit
var_curpos:
var_curposy:            defb 00h                       ; Cursorspalte (Y)
var_curposx:            defb 00h                       ; Cursorzeile  (X)
var_txtcolor:           defb 00h
 

Auf dem Bildschirm sieht das dann so aus (Ich habe das Programm auf dem VZEMU-Emulator getestet. Der emuliert den MC6847 und ist auch sonst dem Z80Ardu nicht unähnlich):



Gruß
Uni
Hi Uni,
schön zu sehen wie es mit der Textausgabe voran geht.


Gruß

Titanes

Gesendet von meinem SM-T800 mit Tapatalk
Zitieren


Gehe zu:


Benutzer, die gerade dieses Thema anschauen: 1 Gast/Gäste