{"id":371,"date":"2023-12-15T10:41:05","date_gmt":"2023-12-15T09:41:05","guid":{"rendered":"https:\/\/www.dev-tronic.de\/?p=371"},"modified":"2023-12-15T10:41:05","modified_gmt":"2023-12-15T09:41:05","slug":"z80ardu-soundkarte-mit-sn76489-wip","status":"publish","type":"post","link":"https:\/\/www.dev-tronic.de\/?p=371","title":{"rendered":"Z80Ardu \u2013 Soundkarte mit SN76489 (gestoppt)"},"content":{"rendered":"\n<p class=\"has-primary-color has-text-color\" style=\"font-style:normal;font-weight:700\">Dieses Projekt habe ich gestoppt. Das Prinzip funktioniert. Der abgebildete Prototyp tut, was er soll, n\u00e4mlich Sound wiedergeben. Ich wollte aber eigentlich immer eine Soundqualit\u00e4t haben, die sich mit dem SID des C64 messen kann.<\/p>\n\n\n\n<p class=\"has-primary-color has-text-color\" style=\"font-style:normal;font-weight:700\">Deshalb gibt es das Projekt &#8222;<a href=\"https:\/\/www.dev-tronic.de\/?p=531\">Z80Ardu &#8211; DoubleSID Soundkarte<\/a>&#8222;<\/p>\n\n\n\n<p><\/p>\n\n\n\n<p>Der Baustein SN76489 wurde von Texas Instruments gebaut und in vielen fr\u00fchen Homecomputern und Konsolen eingebaut. Bei dem Baustein handelt es sich um einen &#8222;Digital Complex Sound Generator&#8220; oder auch PSG (programmable sound generator) genannt.<\/p>\n\n\n\n<p>Die hier gezeigte &#8222;Soundkarte&#8220; ist aus der Sicht der Elektronik  wirklich einfach aufgebaut. Aus der Sicht der Programmierung ist es ein wenig komplizierter.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"839\" src=\"https:\/\/www.dev-tronic.de\/wp-content\/uploads\/2021\/11\/Simple_SN76489-1024x839.jpg\" alt=\"\" class=\"wp-image-374\" srcset=\"https:\/\/www.dev-tronic.de\/wp-content\/uploads\/2021\/11\/Simple_SN76489-1024x839.jpg 1024w, https:\/\/www.dev-tronic.de\/wp-content\/uploads\/2021\/11\/Simple_SN76489-300x246.jpg 300w, https:\/\/www.dev-tronic.de\/wp-content\/uploads\/2021\/11\/Simple_SN76489-768x629.jpg 768w, https:\/\/www.dev-tronic.de\/wp-content\/uploads\/2021\/11\/Simple_SN76489-1536x1258.jpg 1536w, https:\/\/www.dev-tronic.de\/wp-content\/uploads\/2021\/11\/Simple_SN76489-2048x1678.jpg 2048w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><figcaption class=\"wp-element-caption\">Prototyp der &#8222;Soundkarte&#8220;. Auf dem gezeigten Bild ist noch ein Quarzoszillator verbaut, der eigentlich nicht ganz passend ist. \u00dcblicherweise w\u00fcrde man 3,57 MHz einsetzen.<\/figcaption><\/figure>\n\n\n\n<p>Hier ist auch noch ein kleines Musikpr\u00f6bchen. Abgespielt \u00fcber den oben gezeigten Prototyp. Der Verst\u00e4rker ist allerdings ein LM386 mit 200-facher Verst\u00e4rkung, was zu starkem Rauschen und Knacken f\u00fchrt. In dem unten gezeigten Schaltplan f\u00fcr die Soundkarte ist die Verst\u00e4rkung auf 20-fach begrenzt.<\/p>\n\n\n\n<p>Am Anfang der H\u00f6rprobe ist leider noch ein Hup-Ton zu h\u00f6ren. Das liegt an meinem schlecht programmierten Player \ud83d\ude00<\/p>\n\n\n\n<div class=\"wp-block-columns is-layout-flex wp-container-core-columns-is-layout-9d6595d7 wp-block-columns-is-layout-flex\">\n<div class=\"wp-block-column is-layout-flow wp-block-column-is-layout-flow\" style=\"flex-basis:33.33%\">\n<figure class=\"wp-block-video\"><video controls src=\"https:\/\/www.dev-tronic.de\/wp-content\/uploads\/2022\/03\/signal-2022-03-30-180026.mp4\"><\/video><figcaption class=\"wp-element-caption\">Bubble Bobble Theme<\/figcaption><\/figure>\n<\/div>\n\n\n\n<div class=\"wp-block-column is-layout-flow wp-block-column-is-layout-flow\" style=\"flex-basis:66.66%\">\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"500\" height=\"500\" src=\"https:\/\/www.dev-tronic.de\/wp-content\/uploads\/2022\/03\/bubble_bobble.jpg\" alt=\"\" class=\"wp-image-399\" srcset=\"https:\/\/www.dev-tronic.de\/wp-content\/uploads\/2022\/03\/bubble_bobble.jpg 500w, https:\/\/www.dev-tronic.de\/wp-content\/uploads\/2022\/03\/bubble_bobble-300x300.jpg 300w, https:\/\/www.dev-tronic.de\/wp-content\/uploads\/2022\/03\/bubble_bobble-150x150.jpg 150w\" sizes=\"auto, (max-width: 500px) 100vw, 500px\" \/><\/figure>\n<\/div>\n<\/div>\n\n\n\n<p>Um den Prototyp testen zu k\u00f6nnen, habe ich ein Programm f\u00fcr den Z80Ardu geschrieben. Basis daf\u00fcr ist ein VGM-Player, der mal f\u00fcr MSX Computer erstellt wurde. Ich selbst bin mit der genauen Programmierung des SN76489 nicht ganz so vertraut (Aber das wird sicher noch).<\/p>\n\n\n\n<p>Da das Programm f\u00fcr den Z80Ardu geschrieben wurde, passt es zun\u00e4chst auch nur f\u00fcr diesen kleinen Computer. Die Wiedergabe wird per Interrupt gesteuert. Beim Z80Ardu wird am Prozessoreingang \/INT \u00fcber die Grafikkarte (mit dem MC6847-Chip)  bei jedem Vertical Sync Pulse ein Interrupt ausgel\u00f6st. Der Grafikchip wird aktuell mit 3,57 Mhz betrieben, was der Color Burst Frequenz von NTSC entspricht (60 Hz).<\/p>\n\n\n\n<p>Unter Ber\u00fccksichtigung dieser Bedingungen, kann das Kernmodul im Programm auch in anderen Computern genutzt werden. Bitte ber\u00fccksichtigt, dass das Programm ein Testprogramm ist. Die einzelnen Programmteile enthalten so einiges an Overhead, da ich es immer wieder f\u00fcr unterschiedliche Testszenarien verwende. <\/p>\n\n\n\n<p>Der eigentliche Player ist ein eigener Programmteil, der f\u00fcr das Abspielen an sich gen\u00fcgt.<\/p>\n\n\n\n<p><\/p>\n\n\n\n<div class=\"wp-block-urvanov-syntax-highlighter-code-block\"><pre class=\"height-mode:2 height:120 lang:z80 decode:true \" title=\"Header\">;**************************************************************************\n;** Z80Ardu-Header\n;** (c) 2019 by Martin de Hoogh\n;**\n;** Variable und Konstanten\n;**************************************************************************\n\nDEBUG:            equ     1\n\n; IO\nVMODEOUT:         equ     040h                                             ; I\/O-Adresse des Video-Mode-Latches \nKEYBIN:           equ     0E0h                                             ; I\/O-Adresse des Keyboards\nIDE:              equ     060h                                             ; I\/O-Adresse der IDE-Schnittstelle\nCFBASE:           equ     060h\nPSG:              equ     0A0h                                             ; I\/O-Adresse SPI\nPSGREG:           equ     0A0h\n\n;VIDRAM:           equ     07000h                                          ; Start des VideoRAM im Speicher (7000h ist lediglich f\u00fcr Testzwecke)\nVIDRAM:           equ     0E000h                                           ; Start des VideoRAM im Speicher\nVIDRAM_HI:        equ     HI vidram                                        ; Highbyte der VideoRAM-Adresse (Textmodus)\nVIDRAM_LAST:      equ     HI vidram +2                                     ; Highbyte der VideoRAM-Adresse (Textmodus)\nVIDRAM_SLINE:     equ     vidram + 020h                                    ; Zweite Zeile am Bildschirm\nVIDRAM_N2LAST:    equ     vidram + 1e0h                                    ; Vorletzte Zeile am (Text)Bildschirm\n                          \nROM_START:        equ     0h\n;ROM_END:          equ     1000h\n                    \n\n;******************************************************************\n; INIT_GRAPHICMODE  ( GRAPHICS = IO-Address $40)\n;\n;  64 x  64 Dots, 4 Colors (Color Graphics One)\n; 128 x  64 Dots, 2 Colors (Resolution Graphics One)\n; 128 x  64 Dots, 4 Colors (Color Graphics Two)\n; 128 x  96 Dots, 2 Colors (Resolution Graphics Two)\n; 128 x  96 Dots, 4 Colors (Color Graphics Three)\n; 128 x 192 Dots, 2 Colors (Resolution Graphics Three)\n; 128 x 192 Dots, 4 Colors (Color Graphics Six)\n; 192 x 256 Dots, 2 Colors (Resolution Graphics Six)\n; \n;\n; INIT VIRTUAL SCREENs  ( GRAPHICS = IO-Address $40)\n; SCR0  -- Screen 0 -- Adresse 00H\n; SCR1  -- Screen 1 -- Adresse 10H\n; SCR2  -- Screen 2 -- Adresse 20H\n; SCR3  -- Screen 3 -- Adresse 30H\n;\n; Can be combined with a graphic mode\n;\n\n; -- Graphicmodes      Adr.   A\/G\nCG0:               equ 000H                   ; Textmode\nCG1:               equ 000H + 08H             ; 64 x 64, 4 Colors\nRG1:               equ 001H + 08H             ; 128 x 64, 2 Colors\nCG2:               equ 002H + 08H             ; 128 x 64, 4 Colors\nRG2:               equ 003H + 08H             ; 128 x 96, 2 Colors\nCG3:               equ 004H + 08H             ; 128 x 96, 4 Colors\nRG3:               equ 005H + 08H             ; 128 x 192, 2 Colors\nCG6:               equ 006H + 08H             ; 128 x 192, 4 Colors\nRG6:               equ 007H + 08H             ; 256 x 192, 2 Colors\n\nCSS:               equ 020h\n\n; -- Screenpages  im Videoram... F\u00fcr den Prozessor bleibt der Videospeicher \n;    jedoch immer an der Adresse E000h\nSCR0:              equ 00H                    ; 0000H - 1FFFH\nSCR1:              equ 10H                    ; 2000H - 3FFFH\nSCR2:              equ 20H                    ; 4000H - 5FFFH\nSCR3:              equ 30H                    ; 6000H - 7FFFH\n\n\n\n\n; Systemvariable\nRAM_END:           equ     0dfffh\n\nCURS_BLINK:        equ     0ffh\n\nvar_curposy:       equ     RAM_END - 1                                     ; Cursorspalte (Y)\nvar_curposx:       equ     var_curposy - 1                                 ; Cursorzeile  (X)\nvar_curpos:        equ     var_curposy\nvar_txtcolor:      equ     var_curposx - 1\nvar_keybcode:      equ     var_txtcolor - 1                                ; Zuletzt bet\u00e4tigte Taste\nvar_curadr:        equ     var_keybcode - 2                                ; Aktuelle Cursoradresse 16 Bit\nvar_blink:         equ     var_curadr - 1                                  ; Blinkz\u00e4hler\n\nvar_Flag1:         equ     var_blink - 1                                   ; Flag 1\n                                                                           ; - Bit 0 - Cursor blinkt = 1, Cursor schweigt = 0\n                                                                           ; - Bit 1\n                                                                           ; - Bit 2\n                                                                           ; - Bit 3 \n                                                                           ; - Bit 4\n                                                                           ; - Bit 5\n                                                                           ; - Bit 6\n                                                                           ; - Bit 7 \n\nvar_Flag2:         equ     var_Flag1 - 1                                   ; Flag 2\n                                                                           ; - Bit 0 - Cursor blinkt = 1, Cursor schweigt = 0\n                                                                           ; - Bit 1\n                                                                           ; - Bit 2\n                                                                           ; - Bit 3 \n                                                                           ; - Bit 4\n                                                                           ; - Bit 5\n                                                                           ; - Bit 6\n                                                                           ; - Bit 7 \n\nvar_curblink:      equ     var_Flag2 -1                                    ; Cursor-Blinkgeschwindigkeit\nvar_save_sp:       equ     var_curblink - 2                                ; Zwischenspeicher f\u00fcr Stackpointer\nvar_AllowNMI:      equ     var_save_sp - 1                                 ; NMI durchlaufen? 0 = nein, &lt;&gt;0 = ja\nvar_VideoMode:     equ     var_AllowNMI-1                                  ; Status des Video-Latch\nvar_IOBuffer:      equ     var_VideoMode-180                               ; 64 Byte IO-Puffer\nvar_DataFlag:      equ     var_IOBuffer-1                                  ; DATA-Flag\nvar_pixcolor:      equ     var_DataFlag-1                                  ; Pixelfarbe f\u00fcr SET\/RESET\nvar_xcord:         equ     var_pixcolor-1                                  ; X-Koordinate f\u00fcr SET\/RESET\nvar_ycord:         equ     var_xcord-1                                     ; Y-Koordinate f\u00fcr SET\/RESET\nvar_takeKey:       equ     var_ycord-1\nSTACK:             EQU     var_takeKey - 2\nRAM_FREE_END:      equ     RAM_END - 4                                     ; Ende freier Speicher                                    \n\n\n\n\n; Sonstige Variable\neos:               equ     00h                                             ; End of string\ncr:                equ     0dh                                             ; Carriage return\nlf:                equ     0ah                                             ; Line feed\nspace:             equ     20h                                             ; Space\ntab:               equ     20h                                             ; Tabulator\n\n\n<\/pre><\/div>\n\n\n\n<div class=\"wp-block-urvanov-syntax-highlighter-code-block\"><pre class=\"lang:z80 decode:true \" title=\"Hauptprogramm\">;***********************************\n;*  Z80Ardu PSG Player             *\n;*                                 *\n;***********************************\n\n                    ORG 0000H\n                    \n\n                    include \"Header.asm\"\n\n\n;******************************************************************\n; Main Program\n;  \n;******************************************************************\n                   di\n                   im 1\n                   jp _start \n       \n                   org 0008h\n_RST8:             ret\n                   nop\n                   nop\n\n                   org 0010h\n_RST10:            ret\n                   nop\n                   nop\n\n                   org 0018h\n_RST18:            ret\n                   nop\n                   nop\n\n                   \n                               \n                   org 0038h\n                   ; *********************************************\n                   ; Einsprung ISR (\/INT)\n                   ; Der MC6847 der Grafikkarte l\u00f6st alle 54 \u00b5S einen Interrupt aus\n                   ; *********************************************\nrst_38:            \n                   push af\n                   push bc\n                   push de\n                   push hl\n                   di\n\n                   call inthndl\n\n                   ld bc,900\nloopi:             dec bc\n                   ld a,b\n                   or c\n                   jr nz, loopi\n                   \n\nnope:              pop hl\n                   pop de\n                   pop bc\n                   pop af\n\n                   ei\n                   reti\n                   \n    \n\n                                  \n                   ; *********************************************\n                   ; Einsprung ISR (\/NMI)\n                   ; Beim Z80Ardu f\u00fcr Tastatureingaben reserviert\n                   ; *********************************************\n                   org 0066h\nrst_66:            retn\n\n; ------------------------------------------------------------------------------------------------------------------------------------\n\n; Hauptprogramm                 \n_start:            \n                   ld sp,STACK                                             ; Stackpointer setzen\n\n                   ld a,CG0 + SCR0                                         ; ...komplett zur\u00fccksetzen\n                   call setVMode\n                   call pause\n\n                   ld a,20h                                                ; Bildschirm l\u00f6schen\n                   call cls \n                   call pause\n                   ld a,20h                                                ; Bildschirm l\u00f6schen\n                   call cls \n\n                   ld hl,_txt_hello                                        ; Begr\u00fcssungstext adressieren\n                   call print                                              ; und ausgeben\n                   call pause                                              ; P\u00e4uschen\n                   \n                   \n                   call vgmplay\n\n\n                   ei\n\n                   call pause\n\n\n                   \n                   \nplay:              \n                   \n                   jr play                                                 ; Und ab in die Schleife\n\n\n                   include \"Utilities.asm\"\n                   include \"Output.asm\"\n                   include \"PSGmod.asm\"\n                   include \"Txt_and_Tables.asm\"\n\n; --&lt; Texte &gt;---------------------------------------------------------\n_txt_hello:        defm    \"Z80ARDU HOMEBREW COMPUTER       \"\n                   defm    \"64K BYTE RAM.                   \"\n                   defm    \"                                \"\n                   defm    \"(C) 2022 M. DE HOOGH            \"\n                   defm    \"                                \"\n                   defm    \" SN76489 PSG PLAYER             \"\n                   defb    0\n\n_txt_pos:          defm    1,7,\"POSITION  \",0\n                   \nvgmdata:           includebinary \"soundfile.vgm\"    \nvgmend:            defb 0\nintallowed:        defb 0x00\nRAM_START:         defb 00h                                       <\/pre><\/div>\n\n\n\n<div class=\"wp-block-urvanov-syntax-highlighter-code-block\"><pre class=\"height-mode:2 height:120 lang:z80 decode:true \" title=\"Ausgaberoutinen\">;**************************************************************************\n;** Z80Ardu-Output\n;** (c) 2019 by Martin de Hoogh\n;**\n;** Diese Routinensammlung ben\u00f6tigt die Datei \"Header.asm\"\n;** Diese Routinensammlung ben\u00f6tigt die Datei \"Txt_and_Tables.asm\"\n;**************************************************************************\n;\n;\n;** Routine         Parameter          Ergebnis                            Beschreibung\n;   print_at        HL = Anfangsadr    Textausgabe                         Ausgabe eines Strings an vorgegebener Position, die ersten Bytes enthalten die Position\n;\n;   print           HL = Anfangsadr    Textausgabe                         Ausgabe einer Zeichenkette\n;\n;   printchar       Zeichen in A       Ein Zeichenausgeben                 Ausgabe eines Zeichens unter Ber\u00fccksichtung der MC6847-Zeichentabellen \n;\n;   calc_pos        b=x, c=y           Bildschirmadresse                   Berechnung einer Bildschirmadress aus X- und Y-Position\n\n \n; *************************************************************************            \n; print_at\n; Ausgabe einer Zeichenkette an X- und Y-Position\n; X- und Y-Position werden als zwei Bytes am Anfang des Strings erwartet\n; Das Ende der Zeichekette wird mit 00h abgeschlossen\n; \n; print_at ruft calc_pos auf, um die Bildschirmadresse f\u00fcr die Ausgabe\n; zu berechnen .\n; Eingabe:      HL = Anfangsadresse der Position und Zeichenkette\n; *************************************************************************\nprint_at:               ld a,(hl)                                          ; X-Position in A\n                        ld (var_curposx),a\n                        ld b,a                                             ; und nach B\n                        inc hl                                             ; HL auf Y-Position\n                        ld a,(hl)                                          ; Y-Position in A\n                        ld (var_curposy),a\n                        ld c,a                                             ; und nach C\n                        call calc_pos                                      ; Position auf dem Schirm kalkulieren\n_print_nextchar:        inc hl                                             ; HL zegt auf den Text\nprint:                  ld a,(hl)                                          ; Ein Zeichen des Strings lesen\n                        cp 00h\n                        jr z, _print_endputs                               ; Stringabschluss gefunden\n\n\n                        call printchar                                     ; Zeichen ausgeben\n                        jr _print_nextchar                                 ; N\u00e4chstes Zeichen im String\n_print_endputs:         ret                \n\n\n; Ein Zeichen auf dem Bildschirm ausgeben (Ohne Positionsangabe)\nprintchar:              push hl                                            ; Alle Register retten\n                        push de\n                        push bc\n\n                        push af                                            ; Auszugebenden Zeichen sichern\n                        ld a,(var_Flag1)\n                        res 0,a                                            ; Bit 0 = 0 = Cursor blinkt nicht\n                        ld (var_Flag1),a\n                        pop af                                             ; Zeichen wieder herstellen\n\n                        ; Zeichen an MC6847-Zeichentabelle anpassen und Sonder-\n                        ; funktionen ausf\u00fchren\n                        cp 20h                                             ; Leerzeichen?\n                        jr z, _printchar_stchr                             ; Ja, dann Bit 6 lassen wie es ist\n                        cp 0dh                                             ; Carriage Return\n                        jr z, _printchar_cr\n                        cp 80h\n                        jr nc, _printchar_stchr                            ; Semi-Graphics\n                        cp 70h\n                        jr nc,_printchar_stchr\n                        \n                        cp 60h\n                        jr nc, _printchar_inverse                          ; Inverse (Bei Kleinbuchstaben)\n\n                        res 6,a                                            ; Bit 6 l\u00f6schen = Code - 64\n                        jr _printchar_stchr\n\n_printchar_inverse:     sub a,20h\n                        set 6,a\n\n_printchar_stchr:       ld hl,(var_curadr)                                 ; Neue Cursorposition laden\n                        ld (hl),a\n                        \n\n                        inc hl\n                        ld (var_curadr),hl\n                    \n                        ; Pr\u00fcfen ob letzte Zeile erreicht\n_printchar_lastline:    push af                                            ; Zeichen wieder auf den Stack\n                        ld a,h                                             ; Highbyte der aktuellen Position nach a\n                        cp VIDRAM_LAST\n                        jr nz, end_putc\n                    \n                        ; Bildschirm scrollen\n                        ld hl, VIDRAM_SLINE\n                        ld de, VIDRAM\n                        ld bc, 0200h -20h\n                        ldir\n\n                        ; Letzte Zeile leeren\n                        ld b, 020h                                         ; Zeilenl\u00e4nge 32 Zeichen\n                        ld hl,VIDRAM_N2LAST\n                        ld a, 020h                                         ; F\u00fcllzeichen = Leerzeichen\n_clrline:               ld (hl),a\n                        inc hl\n                        djnz _clrline\n\n                        ; Cursor-zeiger auf Anfang der letzten Zeile\n                        ld hl, VIDRAM_N2LAST\n                        ld (var_curadr),hl\n\nend_putc:               ld a,(var_Flag1)\n                        set 0,a                                            ; Bit 0 = 1, Cursor blinkt wieder\n                        ld (var_Flag1),a\n\n                        pop af\n                        pop bc\n                        pop de\n                        pop hl\n                        ret\n\n_printchar_cr:          \n                        ld a,00h                                           ; X=0\n                        ld (var_curposx),a                                 ; Speichern\n                        ld b,a                                             ; und nach B\n                        ld a,(var_curposy)                                 ; aktuelle Y-Position nach a\n                        inc a                                              ; Y +1\n                        ld c,a                                             ; Und in C\n                        ld (var_curposy),a                                 ; Speichern\n                        call calc_pos                                      ; Bildschirnadresse errechnen\n                        \n                        jr _printchar_lastline\n\n\n\n; *************************************************************************            \n; Cursorposition berechnen aus X (0 - 31)\n;                          und Y (0 - 15)\n; X-Position nach b \n; Y-Position nach c \n; HL enth\u00e4lt die Bildschirmposition als 16-Bit adresse\n; HL wird au\u00dferdem in var_curadr abgelegt (=Bildschirmadresse)\n; *************************************************************************\ncalc_pos:               push hl\n                        push bc                                            ; X und Y auf den Stack\n                        ld b, 08h\n                        ld d, 00h\n                        ld e, 20h\n                        ld hl, 0000h\n            \npos_loop:               add hl,hl\n                        sla c\n                        jr nc, pos_zero\n                        add hl,de\npos_zero:               djnz pos_loop\n            \n                        pop bc                                             ; X-Wert vom Stack holen\n                        ld c,b\n                        ld b,00h  \n                        add hl,bc\n                        ld bc, VIDRAM\n                        add hl,bc\n                        ld (var_curadr),hl\n                        pop hl\n                        ret                                  \n\n\n; ************************************************************************* \n; Bildschirm l\u00f6schen (volle 6 KB)\n;\n; A enth\u00e4lt das F\u00fcllzeichen (20h f\u00fcr Textbildschimr, 00h f\u00fcr \n; Grafikbildschirm)\n; ************************************************************************* \ncls:                    push hl                                            ; HL auf den Stack\n                        push de                                            ; DE auf den Stack\n                        push bc                                            ; bc auf den Stack\n                        ld hl,VIDRAM     \n                        ld b,0                                             ; 256 * 24 Bytes = 6144 Bytes\n                        ld d,24                         \n_cls_clearit:           ld (hl),a                                          ; den Inhalt von A in den Speicher pr\u00fcgeln\n                        inc hl\n                        djnz _cls_clearit\n                        dec d\n                        jp nz,_cls_clearit\n                        ld hl,VIDRAM\n                        ld (var_curadr),hl\n                        \n                        ld hl,0000h                                        ; Bildschirmpositionen zur\u00fccksetzen\n                        ld (var_curpos),hl\n\n                        pop bc                                             ; BC vom Stack holen\n                        pop de                                             ; DE vom Stack holen\n                        pop hl                                             ; HL vom Stack holen\n                        ret\n\n\n; ************************************************************************* \n; Cursor ausgeben und Blinken lassen\n;\n; Zeichen an der Cursorposition wird invertiert\n; L\u00e4uft innerhalb der Interrrupt-Service-Routine\n; ************************************************************************* \ncursor:                 ld a,(var_Flag1)                                   ; Flag 1 laden\n                        bit 0,a                                            ; Blink-Bit gesetzt?\n                        jr z,cursor_noblink                                ; N\u00f6, dann nicht rum blinken\n\n                        ld a,(var_Flag2)                                   ; Flag 2 laden\n                        bit 0,a                                            ; ist das CR-Bit gesetzt\n                        ret nz                                             ; ja, dann fertig\n\ncursor_blink:           ld hl,var_blink                                    ; Blinkz\u00e4hler laden\n                        dec (hl)                                           ; - 1\n                        ret nz                                             ; nicht null, fertig\n                        \n                        ld a,CURS_BLINK                                    ; Blinkz\u00e4hler neu setzen\n                        ld (var_blink),a                                   ; und speichern\n                        \n                        ld hl,(var_curadr)                                 ; Cursorposition laden\n                        ld a,040h                                          ; Inverse-Bit nach A\n                        xor (hl)                                           ; Zeichen invertieren\n                        ld (hl),a\n                        ret\ncursor_noblink:         ld hl,(var_curadr)                                 ; Cursoradresse laden\n                        ld a,(hl)                                          ; Zeichen an Cursoradresse laden\n                        and 0ffh-40h\n                        ld (hl),a\n                        ret                                                ; Fertig\n                        \n\n\n\n\n; *************************************************************************\n; Videomodus und Memorypage setzen\n; \n; Eingabe: A = Mode + Screen\n; Ausgabe: Keine\n; *************************************************************************\nsetVMode:               di\n                        push bc\n                        ld b,08h                                           ; 255 mal schreiben (einbrennen)\n_setVMode_loop:         out (VMODEOUT),a                                   ; Ausgabe auf Videoport\n                        djnz _setVMode_loop                                ; Wiederholen bis b=0\n                        ld (var_VideoMode),a                               ; Latch-Status speichern\n                        pop bc       \n                        ei\n                        ret\n\n\n\n; *************************************************************************\n; Eingegebenes Zeichen auf dem Bildschirm ausgeben\n; \n; Eingabe: A = Zeichen aus var_keybcode (gef\u00fcllt durch NMI)\n; Ausgabe: eingegebenes Zeichen\n; *************************************************************************\nscr_output:             push hl                                            ; HL auf den stack\n                        ld a,(var_keybcode)                                ; Zuletzt eingegebenes Zeichen laden\n                        cp 0                                               ; Keine Eingabe?\n                        jr z,scr_output_end                                ; Dann Ende\n                        \n                        call printchar                                     ; Zeichen ausgeben\n                        ld a,00h                                           ; A l\u00f6schen\n                        ld (var_keybcode),a                                ; Keycode auf null und wegschreiben\n                        \nscr_output_end:         pop hl\n                        ret\n\n\n; *************************************************************************\n; SET\/RESET\/POINT f\u00fcr Grafik\n; Aus x,y Bildadresse und Bitmaske ermitteln\n; Sollte bei 128 x 64 und 128 x 192 in 4 Farben funktionieren\n;\n; Eingabe: A= Flag (80h f\u00fcr SET, 01h f\u00fcr RESET )\n; *************************************************************************\n                   \nplotpixel:              ld a,80h                                           ; Maske f\u00fcr SET\n                        push af\n                        ld a,(var_ycord)                                   ; Y-Koordinate laden\n                        ld e,a\n                        xor a                                              ; A=0\n                        ld d,a                                             ; D=0\n                        ex de,hl                                           ; y * Zeilenl\u00e4nge (32)\n                        add hl,hl                                          ; x2\n                        add hl,hl                                          ; x4\n                        add hl,hl                                          ; x8\n                        add hl,hl                                          ; x16\n                        add hl,hl                                          ; x32\n                        ex de,hl                                           ; = rel. Zeilenanf.adresse\n                        ld a,(var_xcord)                                   ; X-Wert laden\n                        push af                                            ; und wieder auf den Stack\n                        srl a                                              ; X-Koordinate \/ 4\n                        srl a\n                        add a,e                                            ; + relative Zeilenanfangsadresse\n                        ld e,a                                                      \n                        ld a,d                                             ; + Bildanfangsadresse\n                        or 0E0H                                            ; (Beim Laser ist das 7000H und in meiner Welt E000H)\n                        ld d,a                                             ; DE = Bildadresse\n                        pop af                                             ; X-Koordinate laden\n                        and 3                                              ; Die letzten 2 Bits maskieren (0,1,2,3)\n                        add a,a                                            ; x2 (0,2,4,6)\n                        ld b,a                                             ; in b als Verschiebez\u00e4hler\n                   \n                        ; F\u00fcr SET und RESET Bits in Reg. A und C maskieren\n                        ld c, 3FH                                          ; Grundwert in C laden\n                        ld a,(var_pixcolor)                                ; Farbcode als Grundwert in A\n                        sla a                                              ; in die oberen 2 Bits schieben\n                        sla a\n_pixshift:              rrc a                                              ; mit b als Schiebez\u00e4hler Grundwerte in erf. Position\n                        rrc c                                              ; schieben\n                        djnz _pixshift                                     ; A f\u00fcr SET (OR), C f\u00fcr RESET (AND)\n                   \n                        ld b,a                                             ; SET-Maske in B \u00fcbertragen\n                        ld a,(de)                                          ; Byte aus Bildspeicher laden\n                        and c                                              ; Bits f\u00fcr adressiertes Pixel l\u00f6schen\n                        ld (de),a                                          ; Byte zur\u00fcckschreiben\n                        pop af                                             ; Funktionsflag laden\n                        or a                                               ; RESET -Anweisung?\n                        jp p, _pixend                                      ; Ja, dann fertig\n                        ld a,(de)                                          ; SET --&gt; Byte wieder laden\n                        or b                                               ; Bits f\u00fcr adressiertes Pixel setzen\n                        ld (de),a\n_pixend:                ret                                                \n<\/pre><\/div>\n\n\n\n<div class=\"wp-block-urvanov-syntax-highlighter-code-block\"><pre class=\"lang:z80 decode:true \" title=\"Das Playermodul\">; Init player\nvgmplay:          \n                   ld hl,vgmdata + 0x40                ; Start of VGM, skipping header.\n                   ld (vgmpos),hl\n                   ld hl, 735                          ; VGM Delay (60hz)\n                   ld (vgmdly),hl\n                   ld a,09Fh                           ; mute all channels on psg\n                   out (psgreg),a\n                   ld a,0BFh\n                   out (psgreg),a\n                   ld a,0DFh\n                   out (psgreg),a\n                   ld a,0FFh\n                   out (psgreg),a                   \n                   ret\n\n\n;------------------------------------------------------------------------------\n; Interrupt Handler. Called every 1\/60 second.\n;------------------------------------------------------------------------------\ninthndl:\n                   push hl                             ; Save registers\n                   push bc\n                   push de\n                   push af\n         ; in a,(tmsreg)                    ; Check if VDP int set and reset it.\n         ; bit 7, a\n         ; jr z, exit\n                   ld hl,(vgmdly)                      ; Check if delay has gone past\n                   ld de,735\n                   and 0xff\n                   sbc hl,de\n                   ld (vgmdly),hl\n                   jr nz, exit\n                   ld hl,(vgmpos)                      ; Start processing VGM commands\nnext:              ld a, (hl)\n                   inc hl\n                   ld (vgmpos), hl\n                   cp 0x66                             ; Restart VGM cmd\n                   jr nz, next1\n                   ld hl,vgmdata + 0x40                ; Start of VGM, skipping header.\n                   ld (vgmpos),hl\n                   ld hl, 735                          ; VGM Delay\n                   ld (vgmdly),hl\n                   jr exit\nnext1:             cp 0x4f                             ; Game Gear SN76489 stereo. Ignored\n                   jr nz, sn76\n                   inc hl\n                   jr next\nsn76:              cp 0x50                             ; Write byte to SN76489.\n                   jr nz, waitnn\n                   ld a, (hl)\n                   inc hl\n                   out (psgreg), a\n                   jr next\nwaitnn:            cp 0x61                             ; Wait nn samples\n                   jr nz, wait60\n                   ld a, (hl)\n                   inc hl\n                   ld d, (hl)\n                   inc hl\n                   ld (vgmpos), hl\n                   ld l, a\n                   ld h, d\n                   ld (vgmdly), hl\n                   jr exit\nwait60:            cp 0x62                             ; Wait 735 samples (60Hz)\n                   jr nz, exit\n                   ld (vgmpos),hl\n                   ld hl, 735\n                   ld (vgmdly), hl\nexit:              pop af                              ; Restore registers\n                   pop de\n                   pop bc\n                   pop hl\n                   ret\n\n;------------------------------------------------------------------------------\n; Variables\n;------------------------------------------------------------------------------\nvgmpos:          defw 0\nvgmdly:          defw 0\n;------------------------------------------------------------------------------\n; VGM data\n;------------------------------------------------------------------------------\n;vgmdata:<\/pre><\/div>\n\n\n\n<div class=\"wp-block-urvanov-syntax-highlighter-code-block\"><pre class=\"height-mode:2 height:120 lang:z80 decode:true \" title=\"Utilities\">;**************************************************************************\n;** Z80Ardu-Utilities\n;** (c) 2019 by Martin de Hoogh\n;**\n;** Diese Routinensammlung ben\u00f6tigt \"output.asm\" f\u00fcr die Datenausgabe\n;** Diese Routinensammlung ben\u00f6tigt die Datei \"Header.asm\"\n;** Diese Routinensammlung ben\u00f6tigt die Datei \"Txt_and_Tables.asm\"\n;**************************************************************************\n;\n;\n;** Routine         Parameter                    Ergebnis                  Beschreibung\n;   pause           Keine                        .\/.                       Pausiert ca. 2 Sekungen bei 4MHz\n;   prthex_nibble   A=Nibble (4 Bit)             Hex-Ausgabe               Ausgabe eines Hex-Nibbles an der Cursorposition\n;   prthex_byte     A=Byte                       Hex-Ausgabe               Ausgabe eines Hex-Bytes an der Cursorposition\n;   prthex_word     HL=Word                      Hex-Ausgabe               Ausgabe eines Hey-Worts an der Cursorposition\n;   prt16dec        HL=Word                      Dec-Ausgabe               Ausgabe einer 16Bit Dezimalzahl an der Cursorposition\n;   prt8dec         A=Byte                       Dec-Ausgabe               Ausgabe einer 8Bit Dezimalzahl an der Cursorposition\n;   checkRAM        Keine                        Bei Fehler: Meldung       Ausgabe einer Meldung bei fehlerhaftem Speicher \n;   strcmp          HL,DE                        Strings vergleichen       Z=1, wenn Stings gleich\n;   strncmp         HL,DE,BC                     Strings vergleichen       Z=1 wenn Teilstrings gleich\n;   strcpy          HL, DE                       String kopieren           Kopiert einen String von (HL) nach (DE)\n;   strncpy         HL,DE,BC                     Teilstring kopieren       Kopiert n (BC) Zeichen von (HL) nach (DE) \n;   strlen          HL=Stringadresse             Stringl\u00e4nge feststellen   Stringl\u00e4nge des Strings an (HL) in BC\n;**************************************************************************\n; Pausenschleife\n; Darauf achten, dass immer eine gerade Anzahl von ex (sp),ix vorhanden \n; ist, sonst wird der Stack zerlegt und ein Programm wird sich auf-\n; h\u00e4ngen\n;**************************************************************************\npause:            push bc                                                  ; BC retten\n                  ld bc,7000h                                              ; 65535 Durchl\u00e4ufe\n                   \n_pause_ploop:     dec bc\n                  ld a,b\n                  or c        \n                  ex (sp),ix                                               ; dieser Befehl dauert einfach recht lange :-)\n                  ex (sp),ix                                               ; ist also ein Dummy\n                  ex (sp),ix\n                  ex (sp),ix\n                  ex (sp),ix\n                  ex (sp),ix\n                                   \n                  jr nz,_pause_ploop\n                                                                           ; So lange wiederholen, bis BC = 0\n                  pop bc                                                   ; BC vom Stack holen\n                  ret                                                      ; Und Ende\n\n\n\n;**************************************************************************\n; Ein Byte Hexadezimal auf dem Bildschirm ausgeben\n;\n; Eingabe: A = Byte\n; Ausgabe: Hexadezimale Ausgabe an Cursorposition\n;**************************************************************************\nprthex_byte:    push    af                                                 ; Save the contents of the registers\n                push    bc\n                ld      b, a\n                rrca\n                rrca\n                rrca\n                rrca\n                call    prthex_nibble                                      ; Print high nibble\n                ld      a, b\n                call    prthex_nibble                                      ; Print low nibble\n                pop     bc                                                 ; Restore original register contents\n                pop     af\n                ret\n\n\n;**************************************************************************\n; Ein Nibble Hexadezimal auf dem Bildschirm ausgeben\n;\n; Eingabe: A = Nibble (die unteren 4 Bit)\n; Ausgabe: Hexadezimale Ausgabe an Cursorposition\n;**************************************************************************\nprthex_nibble:  push    af                                                 ; Der Inhalt von A bleibt erhalten -&gt; Stack\n                and     0fh                                                ; Nur f\u00fcr den Fall....\n                add     a,'0'                                              ; Wenn es 0 ist, sind wir fertig.\n                cp      03ah                                               ; Ergebnis &gt; \"9\"?\n                jr      c, _prthex_nibb_1\n                add     a,'A' - '0' - 0ah                                  ; \"A\" - \"F\" beachten\n_prthex_nibb_1: call    printchar                                          ; Das Nibble ausgeben\n                pop     af                                                 ; und Register A wiederherstellen\n                ret                                                        ; Fertig\n\n\n;**************************************************************************\n; Ein 16bit Wort Hexadezimal auf dem Bildschirm ausgeben\n;\n; Eingabe: HL = 16 Bit-Wort \n; Ausgabe: Hexadezimale Ausgabe an Cursorposition\n;**************************************************************************\nprthex_word:    push    hl                                                 ; HL auf den Stack\n                push    af                                                 ; A (und F) auf den Stack\n                ld      a, h                                               ; Highbyte nach A\n                call    prthex_byte                                        ; Byte ausgeben\n                ld      a, l                                               ; Lowbyte nach A\n                call    prthex_byte                                        ; Byte ausgeben\n                pop     af                                                 ; AF wiederherstellen\n                pop     hl                                                 ; HL wiederherstellen\n                ret                                                        ; Fertig\n\n;**************************************************************************\n; Ein 32bit Wort Hexadezimal auf dem Bildschirm ausgeben\n;\n; Eingabe: BC = 16 Bit-Wort, DE 16 Bit Wort \n; Ausgabe: Hexadezimale Ausgabe an Cursorposition\n;**************************************************************************\nprthex_dword:   push hl                                                    ; HL auf den stack\n                push bc                                                    ; BC auf den Stack\n                pop hl                                                     ; BC nach HL\n                call prthex_word                                           ; 16 Bit ausgeben\n                push de                                                    ; DE auf den Stack\n                pop hl                                                     ; DE nach HL\n                call prthex_word                                           ; 16 Bit ausgeben\n                pop hl\n                ret\n\n\n\n;**************************************************************************\n; Ein 16bit Wort dezimal auf dem Bildschirm ausgeben\n;\n; Eingabe: HL = 16 Bit-Wort \n; Ausgabe: Dezimale Ausgabe an Cursorposition\n; Zerst\u00f6rt: af, bc, hl, de used\n;**************************************************************************\n\n\nprt16dec:       ld bc,-10000\n                call prt16dec_num1\n                ld bc,-1000\n                call prt16dec_num1\n                ld bc,-100\n                call prt16dec_num1\n                ld c,-10\n                call prt16dec_num1\n                ld c,-1\nprt16dec_num1:  ld a,\"0\"-1\nprt16dec_num2:  inc a\n                add hl,bc\n                jr c,prt16dec_num2\n                sbc hl,bc\n                call printchar\n                ret \n\n\n\n\n;**************************************************************************\n; Ein Byte dezimal auf dem Bildschirm ausgeben\n;\n; Eingabe: A = 8 Bit  \n; Ausgabe: Dezimale Ausgabe an Cursorposition\n; Zerst\u00f6rt: af, bc \n;**************************************************************************\nprt8dec:        ld c,-100\n                call prt8dec_num1\n                ld  c,-10\n                call prt8dec_num1\n                ld c,-1\nprt8dec_num1:   ld b,'0'-1\nprt8dec_num2:   inc b\n                add a,c\n                jr c,prt8dec_num2\n                sub c                                                      ;works as add 100\/10\/1\n                push af                                                    ;safer than ld c,a\n                ld  a,b                                                    ;char is in b\n                CALL printchar                                             ;plot a char. Replace with bcall(_PutC) or similar.\nprt8dec_end:    pop af                                                     ;safer than ld a,c\n                ret\n\n\n\n\n;**************************************************************************\n; RAM-Check\n; Der gesamte Speicher wird durchlaufen (ab RAM_START). Es wird ein Byte \n; gelesen. Das Byte wird gesichert, dann ver\u00e4ndert, dann geschrieben, dann\n; gepr\u00fcft, ob der Inhalt stimmt. Wenn der Inhalt stimmt, wird der urspr\u00fcng-\n; liche Wert wiederhergestellt.\n; Im Fehlerfall stoppt die Routine und gibt einen Fehlertext aus.\n;**************************************************************************\ncheckRAM:              push hl\n                       push bc\n                       push de\n\n                       ld hl,txtMemChk\n                       call print \n\n                       ld hl,RAM_START                                     ; Pr\u00fcfung beginnt ab dem Ende des \"Betriebssystems\"\n                       ld bc,0dfffh - RAM_START                            ; Und geht bis zum Bildschirmspeicher\ncheckLoop:             ld a,(hl)                                           ; Byte aus der Speicherstelle laden\n                       ld e,a                                              ; und in E sichern\n                       ld a,r                                              ; Bit 7 setzen\n                       ld d,a                                              ; und in D sichern\n                       ld (hl),a                                           ; Byte in den Speicher schreiben\n                       ld a,(hl)                                           ; und wieder lesen\n                       cp d                                                ; mit D vergleichen\n                       call nz,checkFail                                   ; keine \u00dcbereinstimmung\n                       ld a,e                                              ; gesichertes Byte zur\u00fcckholen\n                       ld (hl),a                                           ; und ins RAM schreiben\n                       inc hl                                              ; Adresse hochz\u00e4hlen\n                       dec bc                                              ; bis BC = 0\n                       ld a,b\n                       or c                                                ; ist BC denn schon 0?\n                       jr nz, checkLoop                                    ; N\u00f6\n\n                       ld hl,txtOK\n                       call print\n\n                       jr checkDone\n\ncheckFail:             push hl\n                       ld hl,txtMemFail                                    ; Fehler im Speicher\n                       call print_at\n                       pop hl\n                       call prtHex_Word                                    ; an Adresse HL\n\ncheckDone:             pop de\n                       pop bc\n                       pop hl\n                       ret\n\n\n;**************************************************************************\n; Name:       strcmp\n; Funktion:   Stringvergleich\n; Eingabe:    HL, DE = addresses of strings to compare\n; Ausgabe:    Zero flag gesetzt, wenn Strings gleich\n;**************************************************************************\n\nstrcmp:               push      bc\n                      ld        bc,0xffff                                  ; max out counter\n                      call      strncmp\n                      pop       bc\n                      ret\n\n;**************************************************************************\n; Name:       strncmp\n; Funktion:   Compare first n characters of strings\n; Eingabe:    HL, DE = Adressen der zu vergleichenden Strings\n;             BC = Anzahl der zu vergleichenden Zeichen (n)\n; Ausgabe:    Zero flag gesetzt, wenn Strings gleich\n;**************************************************************************\n\nstrncmp:              ld        a,b                                          ; check if counter is 0\n                      or        c\n                      jp        z,strncmp_end\n                      ld        a,(de)                                       ; compare bytes\n                      cp        (hl)\n                      jp        nz,strncmp_end\n                      cp        0                                            ; end of first string?\n                      jp        z,strncmp_check\n                      inc       de\n                      inc       hl\n                      dec       bc\n                      jp        strncmp\nstrncmp_check:                                                             ; check end of second string\n                      or        (hl)                                          ; has been reached too\n                      cp        0\nstrncmp_end:\n                      ret\n\n\n;**************************************************************************\n; Name:       strncpy\n; Funktion:   Copy a string\n; Eingabe:    HL = address of source string\n;             DE = address of destination buffer\n; Ausgabe:    keine\n;**************************************************************************\nstrcpy:\n                    push      bc\n                    ld        bc,0xffff ; max out counter\n                    call      strncpy\n                    pop       bc\n                    ret\n\n\n;**************************************************************************\n; Name:       strncpy\n; Funktion:   Copy first n characters of a string\n; Eingabe:    HL = address of source string\n;             DE = address of destination buffer\n;             BC = number of characters to copy (n)\n; Ausgabe:    Keine\n;**************************************************************************\nstrncpy:\n                   ld        a,b\n                   or        c\n                   jp        z,strncpy_end\n                   ld        a,(hl)\n                   ld        (de),a\n                   inc       hl\n                   inc       de\n                   dec       bc\n                   cp        0\n                   jp        nz,strncpy\nstrncpy_end:\n                   ret\n\n\n;**************************************************************************\n; Name:       strlen\n; Funktion:   Find length of string (excluding terminating \\0)\n; Eingabe:    HL = address of string\n; Ausgabe:    BC = length of string\n;**************************************************************************\nstrlen:\n                   ld        bc,0\nstrlen_loop:\n                   ld        a,(hl)\n                   cp        0\n                   jp        z,strlen_end\n                   inc       hl\n                   inc       bc\n                   jp        strlen_loop\nstrlen_end:\n                   ret\n\n\n\n<\/pre><\/div>\n\n\n\n<div class=\"wp-block-urvanov-syntax-highlighter-code-block\"><pre class=\"height-mode:2 height:120 lang:z80 decode:true \" title=\"Texte und Variable\">;;**************************************************************************\n;** Z80Ardu-Texte und Tabellen\n;** (c) 2019 by Martin de Hoogh\n;**\n;**************************************************************************\n;\n\n; Willkommenstext \"Z80Ardu\" in Blockgrafik\ntxtwelcome:       defb 00h,00h                                             ; X- und Y-Position\n                  defm 0a0h,0a3h,0a3h,0a3h,0a3h,0a3h,0a0h,0a1h,0a3h,0a2h,0a0h,0a1h,0a3h,0a2h,0a0h,0a0h,0a0h,0a0h,0a0h,0a0h,0a0h,0a0h,0a0h,0a0h,0a0h,0a0h,0a0h,0a0h,0a0h,0a0h,0a0h,0a0h,0dh\n                  defm 090h,090h,090h,090h,090h,096h,090h,09ah,090h,095h,090h,09ah,090h,095h,090h,090h,090h,090h,090h,090h,090h,090h,090h,090h,090h,090h,090h,090h,090h,090h,090h,090h,0dh\n                  defm 0a0h,0a0h,0a0h,0a0h,0a6h,0a0h,0a0h,0aah,0a0h,0a5h,0a0h,0aah,0a0h,0a5h,0a0h,0a0h,0a0h,0a0h,0a0h,0a0h,0a0h,0a0h,0a0h,0a0h,0a5h,0a0h,0a0h,0a0h,0a0h,0a0h,0a0h,0a0h,0dh\n                  defm 090h,090h,090h,096h,090h,090h,090h,099h,093h,096h,090h,09ah,090h,095h,090h,090h,090h,090h,090h,090h,090h,090h,090h,090h,095h,090h,090h,090h,090h,090h,090h,090h,0dh\n                  defm 0a0h,0a0h,0a6h,0a0h,0a0h,0a0h,0a0h,0aah,0a0h,0a5h,0a0h,0aah,0a0h,0a5h,0a0h,0a6h,0ach,0a9h,0a1h,0ach,0ach,0a2h,0a6h,0ach,0adh,0a5h,0a0h,0a0h,0aah,0a0h,0a0h,0a0h,0dh\n                  defm 090h,096h,090h,090h,090h,090h,090h,09ah,090h,095h,090h,09ah,090h,095h,090h,09ah,093h,095h,095h,090h,090h,090h,09ah,090h,095h,095h,090h,090h,09ah,090h,090h,090h,0dh\n                  defm 0a0h,0ach,0ach,0ach,0ach,0ach,0a0h,0a4h,0ach,0a8h,0a0h,0a4h,0ach,0a8h,0a0h,0a8h,0a0h,0a4h,0a4h,0a0h,0a0h,0a0h,0a4h,0ach,0a8h,0a0h,0ach,0ach,0a0h,0a0h,0a0h,0a0h,0dh\n                  defm \"(C) 2016-20 BY M. DE HOOGH\",0dh,0\n\n; Free Memory\ntxtFreeMem:       defb 00h,08h                                             ; X- und Y-Position\n                  defm \"FREE MEMORY: \",0\n\n; Memory Failure\ntxtMemFail:       defm 0,0,\"MEMORY CHECK FAILED AT: \",0\n\n; Memory Check\ntxtMemChk:        defm 0,0,\"MEMORY CHECK \",0\n\n; Bytes\ntxtBytes:         defm \" BYTES\",0\n\n; OK\ntxtOK:            defm \" OK\",cr,0\n\n; LetsSay\ntxtLetsSay:       defm 00h,0fh,\"SAY HELLO TO THE WORLD!\",0\n\n; Texte f\u00fcr die Registerausgabe\ntxt_a:                  defm \"A = \",0\ntxt_f:                  defm \"   - F = \",0\ntxt_r:                  defm \"   - R = \",0\ntxt_hl:                 defm 0dh,\"HL= \",0\ntxt_de:                 defm \" - DE= \",0\ntxt_bc:                 defm \" - BC= \",0\ntxt_ix:                 defm 0dh,\"IX= \",0\ntxt_iy:                 defm \" - IY= \",0\ntxt_sp:                 defm \" - SP= \",0\n\n; -------------------------------------------------------------------------\n\n; Schl\u00fcsselworttabelle\n\ntbl_Keywords:           defm 80h+\"M\",\"ARTIN\"\n                        defm 80h+\"R\",\"EGINA\"\n                        defm 80h+\"M\",\"IRNA\"\n                        defm 80h+\"A\",\"NKE\"\n                        defm 80h+\"R\",\"ALF\"\n                        defm 80h+\"F\",\"RITZ\"\n                        defb 80h,00h                                       ; Ende\n\ntxt_MusicPointer:       defm 0,8,\"MUSICPOINTER:\",0\ntxt_Substring:          defm 0,9,\"SUBSTR RETURN:\",0\n\n\nende:                   defb      0ffh<\/pre><\/div>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"724\" src=\"https:\/\/www.dev-tronic.de\/wp-content\/uploads\/2022\/03\/Schematic_Z80Ardu_Sound_SN76489_2022-03-30--1024x724.png\" alt=\"\" class=\"wp-image-396\" srcset=\"https:\/\/www.dev-tronic.de\/wp-content\/uploads\/2022\/03\/Schematic_Z80Ardu_Sound_SN76489_2022-03-30--1024x724.png 1024w, https:\/\/www.dev-tronic.de\/wp-content\/uploads\/2022\/03\/Schematic_Z80Ardu_Sound_SN76489_2022-03-30--300x212.png 300w, https:\/\/www.dev-tronic.de\/wp-content\/uploads\/2022\/03\/Schematic_Z80Ardu_Sound_SN76489_2022-03-30--768x543.png 768w, https:\/\/www.dev-tronic.de\/wp-content\/uploads\/2022\/03\/Schematic_Z80Ardu_Sound_SN76489_2022-03-30--100x70.png 100w, https:\/\/www.dev-tronic.de\/wp-content\/uploads\/2022\/03\/Schematic_Z80Ardu_Sound_SN76489_2022-03-30-.png 1169w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><figcaption class=\"wp-element-caption\">Soundkarte f\u00fcr den Z80Ardu mit zwei SN76489 und zwei kleinen Vorverst\u00e4rkern. Bislang ist das so noch ungetestet (Stand 30.03.2022). Der gezeigte Quarzoszillator ist im Plan nicht spezifiziert. Darauf muss man also achten. Und das verwendete Symbol ist eine Eigenkreation. Auf einer Platine kann das dann ein Oszillator im Format DIP14 oder DIP8 sein. Da wollte ich f\u00fcr mich ein wenig Freiheit und Flexibilit\u00e4t haben. <\/figcaption><\/figure>\n\n\n\n<p>Die oben gezeigte Version <strong>FUNKTIONIERT LEIDER NICHT<\/strong>! Die Verst\u00e4rkerstufen rauschen viel zu stark!!<\/p>\n\n\n\n<p><\/p>\n\n\n\n<p>Im Juni 2022 habe ich eine neue Version ohne Verst\u00e4rker entworfen. Der SN76489 funktioniert ganz hervorragend, wenn man am Audioausgang einen Kondensator anschlie\u00dft. Das Audiosignal hat dann einen ordentlichen Pegel und es rauscht auch nicht.<\/p>\n\n\n\n<p>Meine neuen Platinen haben dennoch nicht funktioniert, da sich im Gerberfile zwei Fehler eingeschlichen hatten. Naja, dann auf ein Neues!<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Dieses Projekt habe ich gestoppt. Das Prinzip funktioniert. Der abgebildete Prototyp tut, was er soll, n\u00e4mlich Sound wiedergeben. Ich wollte aber eigentlich immer eine Soundqualit\u00e4t haben, die sich mit dem SID des C64 messen kann. Deshalb gibt es das Projekt &#8222;Z80Ardu &#8211; DoubleSID Soundkarte&#8222; Der Baustein SN76489 wurde von Texas Instruments gebaut und in vielen fr\u00fchen Homecomputern und Konsolen eingebaut. Bei dem Baustein handelt es sich um einen &#8222;Digital Complex Sound Generator&#8220; oder auch PSG (programmable sound generator) genannt. Die<\/p>\n","protected":false},"author":10,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1,3],"tags":[38,36,37,10],"class_list":["post-371","post","type-post","status-publish","format-standard","hentry","category-allgemein","category-z80ardu","tag-audio","tag-sn76489","tag-sound","tag-z80ardu"],"_links":{"self":[{"href":"https:\/\/www.dev-tronic.de\/index.php?rest_route=\/wp\/v2\/posts\/371","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.dev-tronic.de\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.dev-tronic.de\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.dev-tronic.de\/index.php?rest_route=\/wp\/v2\/users\/10"}],"replies":[{"embeddable":true,"href":"https:\/\/www.dev-tronic.de\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=371"}],"version-history":[{"count":14,"href":"https:\/\/www.dev-tronic.de\/index.php?rest_route=\/wp\/v2\/posts\/371\/revisions"}],"predecessor-version":[{"id":539,"href":"https:\/\/www.dev-tronic.de\/index.php?rest_route=\/wp\/v2\/posts\/371\/revisions\/539"}],"wp:attachment":[{"href":"https:\/\/www.dev-tronic.de\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=371"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.dev-tronic.de\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=371"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.dev-tronic.de\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=371"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}