Intel 8080 kit

Kącik dla elektroniki retro - układy, urządzenia, podzespoły, literatura itp.
tapy
User
User
Posty: 139
Rejestracja: niedziela 14 kwie 2019, 17:09
Kontaktowanie:

Re: Intel 8080 kit

Postautor: tapy » poniedziałek 12 paź 2026, 17:02

Bardziej miałem na myśli pełne 16-bit, więc osobiście poszedłbym w stronę V50, bo jeszcze jest dostępny (V40 też niczego sobie, to taki V20 na sterydach). Dlaczego taki kaprys? Optymalizuję port Basic z Z80 na Z8000, męcząc "testem" Mandelbrot i po zmianach wyszło mi, że Z8002 taktowany 6MHz deklasuje V20 z zegarem 10MHz zarówno w trybie 8080 jak i x86. Dlatego chcę sprawdzić czy to jest wina 8-bitowej szyny, czy te procesory są naprawdę aż tak "biedne", czy może moja optymalizacja jest lepsza, niż oryginalny soft Microsoft na x86. To dużo pytań, bez odpowiedzi.

Awatar użytkownika
Zegar
User
User
Posty: 347
Rejestracja: wtorek 02 lip 2019, 14:42

Re: Intel 8080 kit

Postautor: Zegar » poniedziałek 12 paź 2026, 18:41

tapy pisze:Bardziej miałem na myśli pełne 16-bit, więc osobiście poszedłbym w stronę V50, bo jeszcze jest dostępny (V40 też niczego sobie, to taki V20 na sterydach).

V50 to już mikrokontroler z peryferiami w środku. Odpowiednikiem 8086 jest V30. Mam nawet płytę z PC/XT z tym procesorem. Fajnie byłoby znowu się pobawić. Niestety padła mi klawiatura XT, więc jest mały kłopot. ;)
"If A = success, then the formula is A = X + Y + Z.
X is work. Y is play. Z is keep your mouth shut."
A. Einstein

Awatar użytkownika
gaweł
Geek
Geek
Posty: 1350
Rejestracja: wtorek 24 sty 2017, 22:05
Lokalizacja: Białystok

Re: Intel 8080 kit

Postautor: gaweł » środa 14 paź 2026, 21:45

ik8080_il41.png


Obsługa układu 8255

Wspomniany układ jest bardzo popularnym programowalnym portem równoległym. Zawiera w sobie dwa porty PORTA oraz PORTB jako porty 8-bitowe, które można skonfigurować do pracy jako wejście albo jako wyjście (oba w sposób niezależny). Do tego dochodzi kolejny port (PORTC), który może być podzielony na dwa 4-bitowe, z których każda połówka ma możliwość niezależnego skonfigurowania kierunku pracy. Dodatkowo istnieje możliwość manipulowania pojedynczymi bitami PORTC. Nad tym wszystkim „panuje” rejestr sterujący: zapisy do tego rejestru definiują tryb i kierunek pracy jak również odpowiednie zapisy manipulują pojedynczymi bitami (w zakresie ustawienia na stan jedynki jak i zera). Te dwa podstawowe rodzaje poleceń są rozróżniane jednym bitem w wysyłanych do portu kontrolnego danych: najbardziej znaczący bit = 1 to zapis dotyczący trybu i kierunku pracy wszystkich portów. W sytuacji gdy ten bit = 0, zapis dotyczy ustawienia/zerowania wskazanego bitu PORTC. Ta cecha została wykorzystana w obsłudze alfanumerycznego wyświetlacza LCD jak i do obsługi klawiatury. PORTA i połowa PORTC obsługuje moduł LCD, PORTB i druga połowa PORTC obsługuje klawiaturę matrycową. W przypadku modułu LCD, kierunek pracy PORTA jest konfigurowany w zależności od potrzeb (zapis danych oraz poleceń do LCD to wyjście, odczyt statusu LCD to wejście). Dolna połowa PORTC obsługuje linie sterujące LCD (RS, RW i E). Do obsługi klawiatury, PORTB jest wczytującym stan kolumn klawiatury (zawsze wejściem) oraz druga połowa PORTC wymusza stan na liniach rzędów klawiatury (zawsze wyjściem).
ik8080_il42.png

Konfiguracja portów to zapis mvi a , 080h + P8255A_out… , gdzie zawsze jest ustawiony najbardziej znaczący bit (80h) i definiuje kierunki pracy portów w zależności od potrzeb. Port 8255 jest identyfikowany w systemie przez adresy w przestrzeni adresowej IO (LCDPort i KBDPort mają ten sam adres, a istnienie dwóch identyfikatorów zwiększa jedynie czytelność programu źródłowego).

Kod: Zaznacz cały

LCDPort                 .equ    0A0h
KBDPort                 .equ    0A0h

( . . . )

P8255PortA              .equ    0
P8255PortB              .equ    1
P8255PortC              .equ    2
P8255Control            .equ    3


Kod: Zaznacz cały

P8255A_out              .equ    0000000b
P8255A_inp              .equ    0010000b
P8255B_out              .equ    0000000b
P8255B_inp              .equ    0000010b
P8255CL_out             .equ    0000000b
P8255CH_out             .equ    0000000b
P8255CL_inp             .equ    0000001b
P8255CH_inp             .equ    0001000b

( . . . )

SetLCDOutputDataBus                        ;procedure SetLCDOutputDataBus ;
                                           ;begin (* SetLCDOutputDataBus *)
                                           ; LCDPort.PortA := <output> ;
        mvi     a , 080h + P8255A_out + P8255B_inp + P8255CL_out + P8255CH_out
        out     LCDPort + P8255Control     ;
        ret                                ;end (* SetLCDOutputDataBus *) ;
;*******************************************
SetLCDInputDataBus                         ;procedure SetLCDInputDataBus ;
                                           ;begin (* SetLCDInputDataBus *)
                                           ; LCDPort.PortA := <input> ;
        mvi     a , 080h + P8255A_inp + P8255B_inp + P8255CL_out + P8255CH_out
        out     LCDPort + P8255Control     ;
        ret                                ;end (* SetLCDInputDataBus *) ;

Linie sterujące modułem LCD (RS, RW i E) są obsługiwane przez połowę PORTC (jako wyjście) jako niezależne sterowaniu odpowiednimi liniami.
ik8080_il43.png

Kod: Zaznacz cały

SetLCDEBit              .equ    00000101b
ClrLCDEBit              .equ    00000100b
SetLCDRWBit             .equ    00000011b
ClrLCDRWBit             .equ    00000010b
SetLCDRSBit             .equ    00000001b
ClrLCDRSBit             .equ    00000000b

( . . . )

EPulse                                     ;procedure EPulse ;
                                           ;begin (* EPulse *)
        mvi     a , SetLCDEBit             ; LCDPort.PortC[E]:= 1 ;
        out     LCDPort + P8255Control     ;
        call    LCDDelay                   ; LCDDelay ;
        mvi     a , ClrLCDEBit             ; LCDPort.PortC[E]:= 0 ;
        out     LCDPort + P8255Control     ;
        ret                                ;end (* EPulse *) ;
;*******************************************
LCDOutC                                    ;procedure LCDOutC ( Data[A] : byte ) ;
                                           ;begin (* LCDOutC *)
        push    psw                        ;
        call    SetLCDOutputDataBus        ; SetLCDOutputDataBus ;
        mvi     a , ClrLCDRSBit            ; LCDPort.PortC[RS]:= 0 ;
        out     LCDPort + P8255Control     ;
        mvi     a , ClrLCDRWBit            ; LCDPort.PortC[RW]:= 0 ;
        out     LCDPort + P8255Control     ;
        pop     psw                        ;
        out     LCDPort + P8255PortA       ; LCDPort.PortA := Data ;
        call    EPulse                     ; EPulse ;
        ret                                ;end (* LCDOutC *) ;

Jeżeli chodzi o klawiaturę, to cyklicznie w przerwaniach od czasu poszukiwany jest naciśnięty klawisz. Po wymuszeniu stanu na liniach rzędów wczytywany jest stan kolumn.

Kod: Zaznacz cały

HardvSetRowKbd                             ;procedure HardvSetRowKbd ( Row[A] : byte ) ;
                                           ;begin (* HardvSetRowKbd *)
                                           ; case KbdModInstance.RowCounter of
        lda     KbdModInstance + KbdModInstanceT.RowCounter
        cpi     0                          ;  0 :
        jnz     HvdSeRSrv_1                ;
        mvi     a , 00001000b              ;   KBDPort.PortC[4] := 0 ;
        out     KBDPort + P8255Control     ;
        mvi     a , 00001011b              ;   KBDPort.PortC[5] := 1 ;
        out     KBDPort + P8255Control     ;
        mvi     a , 00001101b              ;   KBDPort.PortC[6] := 1 ;
        out     KBDPort + P8255Control     ;
        mvi     a , 00001111b              ;   KBDPort.PortC[7] := 1 ;
        out     KBDPort + P8255Control     ;
        jmp     HvdSeRSrv_0                ;
HvdSeRSrv_1                                ;
        cpi     1                          ;  1 :
        jnz     HvdSeRSrv_2                ;
        mvi     a , 00001001b              ;   KBDPort.PortC[4] := 1 ;
        out     KBDPort + P8255Control     ;
        mvi     a , 00001010b              ;   KBDPort.PortC[5] := 0 ;
        out     KBDPort + P8255Control     ;
        mvi     a , 00001101b              ;   KBDPort.PortC[6] := 1 ;
        out     KBDPort + P8255Control     ;
        mvi     a , 00001111b              ;   KBDPort.PortC[7] := 1 ;
        out     KBDPort + P8255Control     ;
        jmp     HvdSeRSrv_0                ;
HvdSeRSrv_2                                ;
        cpi     2                          ;  2 :
        jnz     HvdSeRSrv_3                ;
        mvi     a , 00001001b              ;   KBDPort.PortC[4] := 1 ;
        out     KBDPort + P8255Control     ;
        mvi     a , 00001011b              ;   KBDPort.PortC[5] := 1 ;
        out     KBDPort + P8255Control     ;
        mvi     a , 00001100b              ;   KBDPort.PortC[6] := 0 ;
        out     KBDPort + P8255Control     ;
        mvi     a , 00001111b              ;   KBDPort.PortC[7] := 1 ;
        out     KBDPort + P8255Control     ;
        jmp     HvdSeRSrv_0                ;
HvdSeRSrv_3                                ;
        cpi     3                          ;  3 :
        jnz     HvdSeRSrv_4                ;
        mvi     a , 00001001b              ;   KBDPort.PortC[4] := 1 ;
        out     KBDPort + P8255Control     ;
        mvi     a , 00001011b              ;   KBDPort.PortC[5] := 1 ;
        out     KBDPort + P8255Control     ;
        mvi     a , 00001101b              ;   KBDPort.PortC[6] := 1 ;
        out     KBDPort + P8255Control     ;
        mvi     a , 00001110b              ;   KBDPort.PortC[7] := 0 ;
        out     KBDPort + P8255Control     ;
        jmp     HvdSeRSrv_0                ;
HvdSeRSrv_4                                ; end (* case *) ;
HvdSeRSrv_0                                ;
        ret                                ;end (* HardvSetRowKbd *) ;

( . . . )

HardvInpKbd                                ;function HardvInpKbd : byte[A] ;
                                           ;begin (* HardvInpKbd *)
        in      KBDPort + P8255PortB       ; HardvInpKbd := KBDPort.PortB ;
        ret                                ;end (* HardvInpKbd *) ;


ik8080_il44.png
Nie masz wymaganych uprawnień, aby zobaczyć pliki załączone do tego posta.

Prawdziwe słowa nie są przyjemne. Przyjemne słowa nie są prawdziwe.
Lao Tse

Awatar użytkownika
gaweł
Geek
Geek
Posty: 1350
Rejestracja: wtorek 24 sty 2017, 22:05
Lokalizacja: Białystok

Re: Intel 8080 kit

Postautor: gaweł » poniedziałek 19 paź 2026, 23:02

ik8080_il51.png


Obsługa układu 8250

8250 to układ obsługi transmisji szeregowej z wbudowanym wewnątrz generatorem sygnału taktującego transmisją. Zawiera wewnątrz również podzielnik by uzyskać oczekiwaną prędkość transmisji. W obecnym zastosowaniu układ obsługuje jedynie przesyłanie danych (operowanie liniami modemowymi nie jest aktywowane → wejściowe dla 8250 na schemacie są sprzętowo wysterowane do stanu aktywnego). Nie zaistnieje możliwość zgłoszenia przerwania w wyniku zmian tych linii modemowych (poza tym programowo konfiguruje się ten kontroler jakie generuje on generuje).
ik8080_il52.png

Przed rozpoczęciem transmisji należy go odpowiednio skonfigurować. Realizuje to funkcja UARTHardwareInit z jednym parametrem, gdzie do akumulatora należy wpisać podzielnik determinujący prędkość transmisji (dla 9600 bps wynosi on 12). Dodatkowo należy wyczyścić kolejki FIFO nadajnika oraz odbiornika.

Kod: Zaznacz cały

SerialSpeed9600         .equ    12
( . . . )
     mvi  a , SerialSpeed9600 ; UARTHardwareInit ( SerialSpeed9600 ) ;
     call UARTHardwareInit    ;
     call ResetSerialFifo     ; ResetSerialFifo ;
( . . . )
ResetSerialFifo               ;procedure ResetSerialFifo ;
                              ;begin (* ResetSerialFifo *)
     mvi     a , 0            ; UARTReciev . CycFr := 0 ;
     sta     UARTReciev + RXDRecT.CycFr
     sta     UARTReciev + RXDRecT.CycTo
                              ; UARTReciev . CycTo := 0 ;
     sta     UARTTransm + TXDRecT.CycFr
                              ; UARTTransm . CycFr := 0 ;
     sta     UARTTransm + TXDRecT.CycTo
                              ; UARTTransm . CycTo := 0 ;
     mvi     a , IdleState    ; UARTTransm . State := IdleState ;
     sta     UARTTransm + TXDRecT.State
     ret                      ;end (* ResetSerialFifo *) ;

Zainicjowanie kontrolera 8250 do pracy wymaga wpisania podzielnika prędkości, trybu transmisji, ustawienia wyjściowych linii modemowych oraz określenia jakie przerwania są zgłaszane do 8259. Układ jest 7-komórkowy w przestrzeni adresowej, toteż poszczególne rejestry mają indywidualne adresy w obrębie 8250. Wpisanie podzielnika jest trochę „pokrętne”: w pierwszej kolejności należy wpisać do rejestru sterującego LineControlRegister (ten sam, który służy do określenia trybu transmisji) liczbę 80 hex (ma być ustawiony najbardziej znaczący bit). W kolejnym kroku wpisać do DataRegister (ten sam, do którego wysyła się i odbiera dane szeregowe) część młodszą podzielnika oraz do InterruptEnRegister (ten sam, w którym specyfikuje się generowane przerwania) część starszą (ogólnie podzielnik jest 16-bitowy a do uzyskania prędkości 600 .. 115200 bps jest on jednobajtowy). Później konfiguruje się tryb transmisji, tu używany jest N81 → 8 bitów, bez parzystości i jednym bitem stopu (słowo sterujące w żadnej kombinacji nie zawiera ustawionego najwyższego bitu, więc 8250 nie rozpozna tego zapisu jako późniejszy wpis podzielnika). Przed włączeniem przerwań warto wygarnąć z rejestrów śmieci (aktywacja może spowodować natychmiastowe wygenerowanie przerwania). Na koniec wpis do rejestru InterruptEnRegister określa jakie przerwania generuje 8250 (tu są od nadajnika oraz odbiornika).

Kod: Zaznacz cały

DataRegister            .equ    0
InterruptEnRegister     .equ    1
InterruptIdRegister     .equ    2
LineControlRegister     .equ    3
ModemControlRegister    .equ    4
LineStatusRegister      .equ    5
ModemStatusRegister     .equ    6
( . . . )
N81Mode                 .equ    03h
( . . . )
UARTHardwareInit         ;procedure UARTHardwareInit
                                     ( SerialSpeed[a] : byte ) ;
                         ;begin (* UARTHardwareInit *)
        push    psw      ;
                         ; (* ustawienie w UART rozszerzenia
                         ;   adresowego do zaprogramowania predkosci *)
        mvi     a , 80h  ; Serial8250+LineControlRegister := 80h ;
        out     Serial8250 + LineControlRegister
                         ; (* mlodszy bajt podzielnika predkosci *)
        pop     psw      ; Serial8250+DataRegister := SerialSpeed ;
        out     Serial8250 + DataRegister  ;
                         ; (* starszy bajt podzielnika predkosci *)
        mvi     a , 0    ; Serial8250+InterruptEnRegister := 0 ;
        out     Serial8250 + InterruptEnRegister
                         ; (* zaprogramowania trybu N81Mode *)
        mvi     a , N81Mode
                         ; Serial8250+LineControlRegister := N81Mode ;
        out     Serial8250 + LineControlRegister
                         ; (* zaprogramowania stanu linii modemowych→
                         ;    wszystkie do stanu aktywnego *)
        mvi     a , 0bh  ; Serial8250+ModemControlRegister := 0BH ;
        out     Serial8250 + ModemControlRegister
                         ; (* wygarniecie smieci z roznych rejestrow *)
                         ; ? := Serial8250 + LineStatusRegister ;
        in      Serial8250 + LineStatusRegister
                         ; ? := Serial8250 + ModemControlRegister ;
        in      Serial8250 + ModemControlRegister
                         ; ? := Serial8250 + DataRegister ;
        in      Serial8250 + DataRegister
                         ; ? := Serial8250 + InterruptIdRegister ;
        in      Serial8250 + InterruptIdRegister
                         ; (* zezwolenie dla UART na przerwania od:
                         ;    nadajnik + odbiornik *)
        mvi     a , 03h  ; Serial8250 + InterruptEnRegister ; := 03H ;
        out     Serial8250 + InterruptEnRegister
        ret              ;end (* UARTHardwareInit *) ;

Układ 8250 zgłasza jeden sygnał przerwania (do 8259), jednak by rozpoznać przyczynę przerwania należy wczytać rejestr InterruptIdRegister, gdzie na bitach jest określona przyczyna przerwania. Obsługa przerwania to:

Kod: Zaznacz cały

SerialInterruptService         ;ISR SerialInterruptService ;
                               ; var
                               ;   IIR     : byte ;
                               ;   Port    : byte ;
                               ;begin (* SerialInterruptService *)
        push    psw            ;
        push    b              ;
        push    d              ;
        push    h              ;
SerIntSrv_0                    ; repeat
                               ;  IIR := Serial8250+InterruptIdRegister & 07h ;
        in      Serial8250 + InterruptIdRegister
        ani     07h            ;  if ( IIR & 0x01 ) = 0 then
        cpi     1              ;
        jz      SerIntSrv_5    ;   begin
        rrc                    ;    IIR := IIR >> 1 ;
        ani     03h            ;
                               ;    case IIR of
        cpi     0              ;     0 : (* 0 0 - Modem status *)
        jnz     SerIntSrv_1    ;
                               ;      Port := Serial8250 + ModemStatusRegister ;
        in      Serial8250 + ModemStatusRegister
        jmp     SerIntSrv_0    ;
SerIntSrv_1                    ;     1 : (* 0 1 - transmitter *)
        cpi     1              ;
        jnz     SerIntSrv_2    ;
        lda     UARTTransm + TXDRecT.CycFr
                               ;      if UARTTransm.CycFr = UARTTransm.CycTo then
        lxi     h , UARTTransm + TXDRecT.CycTo
        cmp     m              ;
        jnz     SerIntSrv_11   ;
        mvi     a , IdleState  ;       UARTTransm.State := IdleState ;
        sta     UARTTransm + TXDRecT.State
        jmp     SerIntSrv_0    ;
SerIntSrv_11                   ;      else
                               ;       begin
        lda     UARTTransm + TXDRecT.CycFr
                               ;        Serial8250+DataRegister:= UARTTransm.Buffer[UARTTransm.CycFr] ;
        mov     c , a          ;
        mvi     b , 0          ;
        lxi     h , UARTTransm + TXDRecT.Buffer
        dad     b              ;
        mov     a , m          ;
        out     Serial8250 + DataRegister
                               ;        UARTTransm.CycFr:= UARTTransm.CycFr+1 ;
        lxi     h , UARTTransm + TXDRecT.CycFr
        mov     a , m          ;
        inr     a              ;
        ani     TXDBuffSize - 1;        UARTTransm.CycFr:=UARTTransm.CycFr mod TXDBuffSize ;
        mov     m , a          ;
        jmp     SerIntSrv_0    ;       end ;
SerIntSrv_2                    ;
        cpi     2              ;     2 : (* 1 0 - reciever *)
        jnz     SerIntSrv_3    ;      begin
        lda     UARTReciev + RXDRecT.CycTo
                               ;       UARTReciev.Buffer[UARTReciev.CycTo]:= Serial8250+DataRegister ;
        mov     c , a          ;
        mvi     b , 0          ;
        lxi     h , UARTReciev + RXDRecT.Buffer
        dad     b              ;
        in      Serial8250 + DataRegister
        mov     m , a          ;
                               ;       UARTReciev.CycTo:=UARTReciev.CycTo+1 ;
        lxi     h , UARTReciev + RXDRecT.CycTo
        mov     a , m          ;
        inr     a              ;
        ani     RXDBuffSize - 1;       UARTReciev.CycTo:=UARTReciev.CycTo mod RXDBuffSize ;
        mov     m , a          ;
        jmp     SerIntSrv_0    ;      end ;
SerIntSrv_3                    ;
        cpi     3              ;     3 : (* 1 1 - error *)
        jnz     SerIntSrv_0    ;
                               ;       Port := Serial8250 + LineStatusRegister ;
        in      Serial8250 + LineStatusRegister
        jmp     SerIntSrv_0    ;    end (* case *) ;
                               ;   end
                               ;  else
                               ;   exit ;
                               ; until false ;
SerIntSrv_5                    ;
        mvi     a , 20h        ; end of irq
        out     Controller8259 ;
        pop     h              ;
        pop     d              ;
        pop     b              ;
        pop     psw            ;
        ei                     ;
        ret                    ;

Kontroler transmisji szeregowej opiera się o kolejki FIFO w zakresie nadawania oraz odbierania danych. Ich konstrukcje są następujące:

Kod: Zaznacz cały

IdleState       .equ        0
BusyState       .equ        1
;-------------------------------------------
TXDBuffSize     .equ        128
RXDBuffSize     .equ        16
;-------------------------------------------
TXDRecT         .struct           ; TXDRecT = record
        CycFr   .byte             ;    CycFr  : byte ;
        CycTo   .byte             ;    CycTo  : byte ;
        State   .byte             ;    State  : byte ;
        Buffer  .byte TXDBuffSize ;    Buffer : array [ 0 .. TXDBuffSize - 1 ] of byte ;
                .endstruct        ;  end (* record *) ;
;-------------------------------------------
RXDRecT         .struct           ; RXDRecT = record
        CycFr   .byte             ;    CycFr  : byte ;
        CycTo   .byte             ;    CycTo  : byte ;
        Buffer  .byte RXDBuffSize ;    Buffer : array [ 0 .. RXDBuffSize - 1 ] of byte ;
                .endstruct        ;  end (* record *) ;
;-------------------------------------------
( . . . )
        .org    RAMSpace
UARTTransm      .TXDRecT
UARTReciev      .RXDRecT

Odbiór danych z 8250 wchodzi do UARTRecv bezwarunkowo (najwyżej dane się nadpiszą), realizacja znajduje się w powyższej funkcji obsługi przerwania. Z tą kolejką FIFO związane są dwie funkcja: sprawdzenia, czy w FIFO są dane do odczytania (SerialDataPresent) oraz odczytanie kolejnego bajtu (GetSerial).

Kod: Zaznacz cały

SerialDataPresent              ;function SerialDataPresent : boolean ;
                               ;begin (* SerialDataPresent *)
                               ; if UARTTransm.CycFr = UARTTransm.CycTo then
        lxi     h , UARTTransm + TXDRecT.CycFr
        mov     a , m          ;
        lxi     h , UARTTransm + TXDRecT.CycTo
        cmp     m              ;
        jnz     SerDatPre_0    ;
        mvi     a , 0          ;  SerialDataPresent := false
        ret                    ; esle
SerDatPre_0                    ;
        mvi     a , 1          ;  SerialDataPresent := true ;
        ret                    ;end (* SerialDataPresent *) ;
;*******************************
GetSerial                      ;function GetSerial : byte[a] ;
                               ;var
                               ; Ch[e] : byte ;
                               ;begin (* GetSerial *)
                               ; Ch:=UARTTransm.Buffer[UARTTransm.CycFr] ;
        lda     UARTTransm + TXDRecT.CycFr
        mov     c , a          ;
        mvi     b , 0          ;
        lxi     h , UARTTransm + TXDRecT.Buffer
        dad     b              ;
        mov     e , m          ;
                               ; UARTTransm.CycFr:=(UARTTransm.CycFr+1) mod RXDBuffSize ;
        lxi     h , UARTTransm + TXDRecT.CycFr
        mov     a , m          ;
        inr     a              ;
        ani     RXDBuffSize - 1;
        mov     m , a          ;
        mov     a , e          ; GetSerial := Ch ;
        ret                    ;end (* GetSerial *) ;

Nadawanie danych do 8250 jest realizowane przez SendSerial. Jeżeli UART nie pracuje, to pierwszy znak trafia bezpośrednio do 8250 (z odnotowaniem, że jest on już zajęty), kolejne dane trafiają do kolejki FIFO. Zawsze jest sprawdzane, czy jest miejsce w FIFO na zapis nowych danych. Jeżeli jest, następuje zapis do FIFO. W przeciwnym wypadku realizowane jest oczekiwanie do chwili, gdy w buforze pojawi się wolne miejsce (w wyniku wygarniania danych z tej kolejki przez obsługę przerwania).

Kod: Zaznacz cały

SendSerial                           ;procedure SendSerial ( SerialCh : byte ) ;
                                     ;var
                                     ; NextInx : byte ;
                                     ;begin (* SendSerial *)
        di                           ; DisableInt ;
        push    psw                  ;
                                     ; if UARTTransm . State = IdleState then
        lxi     h , UARTTransm + TXDRecT.State
        mov     a , m                ;
        cpi     IdleState            ;
        jnz     SeSer_1              ;  begin
        mvi     a , BusyState        ;   UARTTransm . State := BusyState ;
        mov     m , a                ;
        pop     psw                  ;
                                     ;   Serial8250 + DataRegister := SerialCh ;
        out     Serial8250 + DataRegister
        ei                           ;   EnableInt ;
        ret                          ;   exit ;
                                     ;  end ;
SeSer_1                              ; repeat
                                     ;  NextInx:=(UARTTransm.CycTo+1) mod TXDBuffSize ;
        lda     UARTTransm + TXDRecT.CycTo
        inr     a                    ;
        ani     TXDBuffSize - 1      ;
                                     ;  if NextInx <> UARTTrans . CycFr then
        lxi     h , UARTTransm + TXDRecT.CycFr
        cmp     m                    ;
        jz      SeSer_2              ;   begin
                                     ;    UARTTrans.Buffer[UARTTrans.CycTo]:=SerialCh ;
        lda     UARTTransm + TXDRecT.CycTo
        mov     c , a                ;
        mvi     b , 0                ;
        lxi     h , UARTTransm + TXDRecT.Buffer
        dad     b                    ;
        pop     psw                  ;
        mov     m , a                ;
                                     ;    UARTTransm.CycTo:=(UARTTransm.CycTo+1) mod TXDBuffSize ;
        lxi     h , UARTTransm + TXDRecT.CycTo
        mov     a , m                ;
        inr     a                    ;
        ani     TXDBuffSize - 1      ;
        mov     m , a                ;
        ei                           ;    EnableInt ;
        ret                          ;    exit ;
SeSer_2                              ;   end ;
        ei                           ;  EnableInt ;
        nop                          ;  Nop ;
        nop                          ;  Nop ;
        nop                          ;  Nop ;
        di                           ;  EisableInt ;
        jmp     SeSer_1              ; until false ;
                                     ;end (* SendSerial *) ;
( . . . )
SendSerialString                     ;procedure SendSerialString ( String[hl] : array of byte ) ;
                                     ;var
                                     ; Data : byte ;
                                     ; Inx  : byte ;
                                     ;begin (* SendSerialString *)
                                     ; Inx := 0 ;
SeSerStr_0                           ; repeat
        mov     a , m                ;  Data = String [ Inx ] ;
        inx     h                    ;  Inx := Inx + 1 ;
        cpi     0                    ;  if Data = 0 then
        jnz     SeSerStr_1           ;
        ret                          ;   exit ;
SeSerStr_1                           ;
        push    h                    ;
        call    SendSerial           ;  SendSerial ( Data ) ;
        pop     h                    ;
        jmp     SeSerStr_0           ; until false ;
                                     ;end (* SendSerialString *) ;

Prezentacja działania obsługi układu 8250 to:

Kod: Zaznacz cały

SerialHello   .defb  "Zestaw edukacyjny z procesorem Intel 8080", 0Dh, 0Ah, 0
( . . . )
   lxi     h , SerialHello    ; SendSerialString ( SerialHello ) ;
   call   SendSerialString    ;

I zadziałało:
ik8080_il53.png

Na obecnym etapie wiadomo, że cały zestaw jest technicznie sprawny (test pamięci RAM również przechodzi z tym samym algorytmem co w poprzedniej iteracji). Teraz zostało trochę potrudzić się nad „systemem operacyjnym”.
Nie masz wymaganych uprawnień, aby zobaczyć pliki załączone do tego posta.

Prawdziwe słowa nie są przyjemne. Przyjemne słowa nie są prawdziwe.
Lao Tse

Awatar użytkownika
gaweł
Geek
Geek
Posty: 1350
Rejestracja: wtorek 24 sty 2017, 22:05
Lokalizacja: Białystok

Re: Intel 8080 kit

Postautor: gaweł » czwartek 29 paź 2026, 23:06

ik8080_il61.png


Uruchomienie ładowanego programu

Jedną z przewidywanych funkcji tego zestawu edukacyjnego jest przećwiczenie ładowanego z zewnątrz programu. By maksymalnie uprościć tą funkcjonalność, program do uruchomienia jest ładowany z zewnątrz (via UART 8250, który jest skonfigurowany na tryb pracy 9600 N81). Są pewne wymagania stawiane ładowanemu programu. Ponieważ kit 8080 ma EPROM 27C128 (16 kB) lokowany jest od adresu 0, to ładowany program ładuje się do pamięci RAM. Początkowa koncepcja podziału całej przestrzeni adresowej 0..16 kB to pamięć EPROM, która wykorzystuje pamięć RAM z przedziału 16 kB..32 kB jako pamięć operacyjną dla EPROM (w tym również stos). Pozostała przestrzeń 32 kB ..64 kB jest przewidziana dla ładowanych programów. Oznacza to, że ładowany program musi być linkowany na ten obszar. Pierwsza (obecna) bardzo uproszczona wersja zakłada, że program jest ładowany do obszaru od adresu 32 kB jak również punkt wejścia do programu znajduje się również w tym miejsce (odpowiednik operacji Reset to start programu od adresu 8000 hex). Również do rozważenia jest możliwość ładowania programu do innego obszaru (ale z zakresu adresowego 32 kB..64 kB, jest to proste do wyczajenia, gdyż w pliku intel-hex jest określona adresacja kodu). Z tym jest związana możliwość uruchomienia programu od innego adresu (do polecenia F5/GO można dorobić możliwość podania adresu).
Pierwszy testowy program jest banalnie prosty, jak na poniższym listingu

Kod: Zaznacz cały

Improved compiler for INTEL 8080/8085 processor, version 2.4 licence: CC BY 3.0

  L.NO MEMO    CODE         SRC

( . . . )
   22.                        .i8080
   23.                 ;
   24. 8000            ResetEntry              .equ 08000h     ;
   25. FFFE            Stack_top               .equ 0fffeh     ;
   26. 00A8            LEDRegister             .equ 0A8h       ;
   27.                 ;*******************************************
   28.                        .org    ResetEntry                  ;
   29. 8000 C33580            jmp     Main                        ;
   30. 8003 03         DemoTable               .defb   00000011b  ;
   31. 8004 06                                 .defb   00000110b  ;
   32. 8005 0C                                 .defb   00001100b  ;
   33. 8006 18                                 .defb   00011000b  ;
   34. 8007 30                                 .defb   00110000b  ;
   35. 8008 60                                 .defb   01100000b  ;
   36. 8009 80                                 .defb   10000000b  ;
   37. 800A 60                                 .defb   01100000b  ;
   38. 800B 30                                 .defb   00110000b  ;
   39. 800C 18                                 .defb   00011000b  ;
   40. 800D 0C                                 .defb   00001100b  ;
   41. 800E 06                                 .defb   00000110b  ;
   42. 800F 03                                 .defb   00000011b  ;
   43. 8010 00                                 .defb   00000000b  ;
   44.                 ;*******************************************
   45. 8011            LEDDemo                                    ;procedure LEDDemo ;
   46.                                                            ;var
   47.                                                            ; TmpPtr : word[hl] ;
   48.                                                            ; TmpCh : byte ;
   49.                                                            ;begin (* LEDDemo *)
   50. 8011 210380            lxi     h , DemoTable               ; TmpPtr := DemoTable ;
   51. 8014            Ledde_3                                    ; repeat
   52. 8014 7E                mov     a , m                       ;  TmpCh := TmpPtr ^ ;
   53. 8015 FE00              cpi     0                           ;  if TmpCh <> 0 then
   54. 8017 CA2580            jz      Ledde_1                     ;  begin
   55. 801A D3A8              out     LEDRegister                 ;   LEDRegister := TmpCh ;
   56. 801C E5                push    h                           ;
   57. 801D CD2B80            call    DemoDelay                   ;   DemoDelay ;
   58. 8020 E1                pop     h                           ;
   59. 8021 23                inx     h                           ;   TmpPtr := TmpPtr  + 1 ;
   60. 8022 C31480            jmp     Ledde_3                     ;  end
   61. 8025            Ledde_1                                    ;  else
   62. 8025 210380            lxi     h , DemoTable               ;   TmpPtr := DemoTable ;
   63. 8028 C31480            jmp     Ledde_3                     ; until false ;
   64.                                                            ;end (* LEDDemo *) ;
   65.                 ;*******************************************
   66. 802B            DemoDelay                                  ;procedure DemoDelay ;
   67.                                                            ;begin (* DemoDelay *)
   68. 802B 010030            lxi     b , 03000h                  ;
   69. 802E            DeDel_0                                    ;
   70. 802E 0B                dcx     b                           ;
   71. 802F 78                mov     a , b                       ;
   72. 8030 B1                ora     c                           ;
   73. 8031 C22E80            jnz     DeDel_0                     ;
   74. 8034 C9                ret                                 ;end (* DemoDelay *) ;
   75.                 ;*******************************************
   76. 8035            Main                                       ;begin (* main *)
   77. 8035            Main_0                                     ;
   78. 8035 CD1180            call    LEDDemo                     ; LEDDemo ;
   79. 8038 C33580            jmp     Main_0                      ; until false ;       
   80.                                                            ;end  (* main *).
   81.                 ;*******************************************
   82.                         .end
Close file: D:\prace\i8080kit\i8080test.asm

Compilation :successful


Map information about constants and labels:
LEDRegister                      CONST=00A8 hex [0000000010101000 bin,168 dec]
ResetEntry                       CONST=8000 hex [1000000000000000 bin,32768 dec]
Stack_top                        CONST=FFFE hex [1111111111111110 bin,65534 dec]
DeDel_0                          LABEL:802E hex
DemoDelay                        LABEL:802B hex
DemoTable                        LABEL:8003 hex
LEDDemo                          LABEL:8011 hex
Ledde_1                          LABEL:8025 hex
Ledde_3                          LABEL:8014 hex
Main                             LABEL:8035 hex
Main_0                           LABEL:8035 hex


Code stored in file: D:\prace\i8080kit\i8080test.hex

Z tego wydruku z kompilacji widać, że program jest przeznaczony do uruchomienia we właściwym obszarze dla kitu 8080 (ResetEntry .equ 08000h oraz jest .org ResetEntry). Wszystkie etykiety dla instrukcji skoków również są z tego obszaru. Zadaniem programu jest demko z dwoma diodami LED.
Kompilacja tego programu generuje plik z zawartością pamięci w formacie intel-hex.

Kod: Zaznacz cały

:10800000C3358003060C183060806030180C0603FE
:10801000002103807EFE00CA2580D3A8E5CD2B80F9
:10802000E123C31480210380C314800100300B7846
:0B803000B1C22E80C9CD1180C3358085
:00000001FF

Realizacja w EPROM załadowania i uruchomienia programu to:

Kod: Zaznacz cały

SelectCommandExecute                       ;procedure SelectCommandExecute ( Ch[a] : byte ) ;
                                           ;begin (* SelectCommandExecute *)
                                           ; case Ch of
       cpi     F1Ch                        ;  F1Ch :
       jnz     SelCmmEx_1                  ;
       call    LoadProgramFromSerial       ;   LoadProgramFromSerial ;
       call    RewindCommandService        ;   RewindCommandService ;
       call    SelectCommand               ;   SelectCommand ;
       ret                                 ;   exit ;
SelCmmEx_1                                 ;  F2Ch :
( . . . )
       cpi     F5Ch                        ;
       jnz     SelCmmEx_5                  ;
       call    ClrScrLCD                   ;   ClrScrLCD ;
       lxi     h , ProgramRunning          ;   WriteTextLCD ( ProgramRunning ) ;
       call    WriteTextLCD                ;
       lxi     h , 8000h                   ;   call 8000h ;
       call    CallHLService               ;
       ret                                 ;   exit ;
( . . . )
       ret                                 ;end (* SelectCommandExecute *) ;

Operacja ładowania kodu via UART zawiera elementarną kontrolę poprawności pliku intel-hex (nie bada jedynie poprawności sumy kontrolnej):

Kod: Zaznacz cały

LoadProgramFromSerial           ;procedure LoadProgramFromSerial ;
                                ;var
                                ; Tmp[A] : byte ;
                                ;begin (* LoadProgramFromSerial *)
     mvi   a , 0                ; SystemInstance.LoadedStatus:=false ;
     sta   SystemInstance + SystemInstanceT . LoadedStatus
     call  ClrScrLCD            ; ClrScrLCD ;
     lxi   h , StartLoadingMsg  ; WriteTextLCD(StartLoadingMsg) ;
     call  WriteTextLCD         ;
LoProgFrSe_0                      ; repeat
     call  GetHexFileLine       ;  if not GetHexFileLine then
     cpi   1                    ;
     jz    LoProgFrSe_1         ;  begin
     call  NewLineLCD           ;   NewLineLCD ;
                                ;   WriteTextLCD(HexFileformatErrorMsg) ;
     lxi   h , HexFileformatErrorMsg
     call  WriteTextLCD         ;
     call  AnyKey               ;   AnyKey ;
     ret                        ;   exit ;
LoProgFrSe_1                    ;  end ;
     mvi   a , 0                ;  SystemInstance . HexLineIndex := 0 ;
     sta   SystemInstance + SystemInstanceT . HexLineIndex
     call  GetHexLineByte       ;  SystemInstance.BlockSize:=GetHexLineByte ;
     sta   SystemInstance + SystemInstanceT . BlockSize
     call  GetHexLineWord       ;  SystemInstance.LoadAddress:=GetHexLineWord ;
     lxi   h , SystemInstance + SystemInstanceT . LoadAddress
     mov   m , e                ;
     inx   h                    ;
     mov   m , d                ;
     call  GetHexLineByte       ;  Tmp := GetHexLineByte ;
     cpi   1                    ;  if Tmp = 1 then
     jnz   LoProgFrSe_2         ;  begin
     mvi   a , 1                ;   SystemInstance.LoadedStatus:=true ;
     sta   SystemInstance + SystemInstanceT . LoadedStatus
     ret                        ;   exit ;
                                ;  end
LoProgFrSe_2                    ;  else
                                ;  begin
LoProgFrSe_4                    ;   while SystemInstance . BlockSize <> 0 do
     lda   SystemInstance + SystemInstanceT . BlockSize
     ora   a                    ;
     jz    LoProgFrSe_3         ;   begin
     call  GetHexLineByte       ;    SystemInstance.LoadAddress^:=GetHexLineByte ;
     lhld  SystemInstance + SystemInstanceT . LoadAddress
     mov   m , a                ;
                                ;    SystemInstance.LoadAddress:=SystemInstance.LoadAddress+1 ;
     lhld  SystemInstance + SystemInstanceT . LoadAddress
     inx   h                    ;
     shld  SystemInstance + SystemInstanceT . LoadAddress
                                ;    SystemInstance.BlockSize:=SystemInstance.BlockSize-1 ;
     lxi   h , SystemInstance + SystemInstanceT . BlockSize
     dcr   m                    ;
     jmp   LoProgFrSe_4         ;   end ;
LoProgFrSe_3                    ;
     jmp   LoProgFrSe_0         ; until false ;
                                ;end (* LoadProgramFromSerial *) ;

Algorytm reaguje na zapis :00000001FF (blok o kodzie 01hex oznaczający koniec pliku intel-hex).
Procedura ładowania kodu z UART realizuje w pętli wczytanie z UART po jednym wierszu danych, analizuje go i ładuje do pamięci RAM.
Wybrane funkcje to:

Kod: Zaznacz cały

AddToHexLine                    ;procedure AddToHexLine ( Ch[A] : byte ) ;
                                ;begin (* AddToHexLine *)
     mov   e , a                ;
                                ; if SystemInstance.HexLineIndex<LoadedHexLineSize then
     lxi   h , SystemInstance + SystemInstanceT.HexLineIndex
     mov   a , m                ;
     cpi   LoadedHexLineSize    ;
     jnc   AddTHL_0             ; begin
     mvi   b , 0                ;  SystemInstance.LoadedHexLine [SystemInstance.HexLineIndex]:= Ch ;
     mov   c , a                ;
     lxi   h , SystemInstance + SystemInstanceT.LoadedHexLine
     dad   b                    ;
     mov   m , e                ;
                                ;  SystemInstance.HexLineIndex:= SystemInstance.HexLineIndex+1 ;
     lxi   h , SystemInstance + SystemInstanceT.HexLineIndex
     inr   m                    ;
AddTHL_0                        ; end ;
     ret                        ;end (* AddToHexLine *) ;
;********************************
GetHexFileLine                  ;function GetHexFileLine : boolean [A] ;
                                ;var
                                ; Ch[A] : byte ;
                                ;begin (* GetHexFileLine *)
     mvi   a , 0                ; SystemInstance . HexLineIndex := 0 ;
     sta   SystemInstance + SystemInstanceT . HexLineIndex
GeHxFiLi_0                      ; repeat
     call  SerialDataPresent    ;  if SerialDataPresent then
     cpi   1                    ;
     jnz   GeHxFiLi_0           ;  begin
     call  GetSerial            ;   Ch := GetSerial ;
     cpi   ':'                  ;   if Ch = ':' then
     jnz   GeHxFiLi_0           ;    break ;
                                ;  end ;
                                ; until false ;
GeHxFiLi_1                      ; repeat
     call  SerialDataPresent    ;  if SerialDataPresent then
     cpi   1                    ;
     jnz   GeHxFiLi_1           ;  begin
     call  GetSerial            ;   Ch := GetSerial ;
     cpi   CrCh                 ;   if Ch = CrCh then
     jnz   GeHxFiLi_2           ;   begin
     mvi   a , 0                ;    AddToHexLine ( 0 ) ;
     call  AddToHexLine         ;
     jmp   GeHxFiLi_1           ;   end ;
GeHxFiLi_2                      ;
     cpi   LfCh                 ;   if Ch = LfCh then
     jnz   GeHxFiLi_3           ;   begin
     mvi   a , 1                ;    GetHexFileLine := true ;
     ret                        ;    exit ;
GeHxFiLi_3                      ;   end ;
     call  AddToHexLine         ;   AddToHexLine ( Ch ) ;
     call  HexDigit             ;   if not HexDigit ( Ch ) then
     cpi   1                    ;
     jnz   GeHxFiLi_1           ;   begin
     mvi   a , 0                ;    GetHexFileLine := false ;
     ret                        ;    exit ;
                                ;   end ;
                                ; until false ;
                                ;end (* GetHexFileLine *) ;
;********************************
GetHexLineChar                  ;function GetHexLineChar : byte [A] ;
                                ;var
                                ; Tmp[A] : byte ;
                                ;begin (* GetHexLineChar *)
                                ; Tmp:= SystemInstance.LoadedHexLine[SystemInstance.HexLineIndex] ;
     lda   SystemInstance + SystemInstanceT.HexLineIndex
     mvi   b , 0                ;
     mov   c , a                ;
     lxi   h , SystemInstance + SystemInstanceT.LoadedHexLine
     dad   b                    ;
     mov   a , m                ;
                                ; SystemInstance.HexLineIndex:= SystemInstance.HexLineIndex+1 ;
     lxi   h , SystemInstance + SystemInstanceT.HexLineIndex
     inr   m                    ;
     ret                        ; GetHexLineChar := Tmp ;
                                ;end (* GetHexLineChar *) ;
;********************************
GetHexLineByte                  ;function GetHexLineByte : byte [A] ;
                                ;var
                                ; ChH : byte ;
                                ; ChL : byte ;
                                ;begin (* GetHexLineByte *)
     call  GetHexLineChar       ; ChH := HexChToBin ( GetHexLineChar ) ;
     call  HexChToBin           ;
     push  psw                  ;
     call  GetHexLineChar       ; ChL := HexChToBin ( GetHexLineChar ) ;
     call  HexChToBin           ;
     mov   b , a                ; GetHexLineByte := ChH << 4 + ChL ;
     pop   psw                  ;
     rlc                        ;
     rlc                        ;
     rlc                        ;
     rlc                        ;
     ora   b                    ;
     ret                        ;end (* GetHexLineByte *) ;
;********************************
GetHexLineWord                  ;function GetHexLineWord : word [DE] ;
                                ;var
                                ; HiPart : word ;
                                ; LoPart : word ;
                                ;begin (* GetHexLineWord *)
     call  GetHexLineByte       ; HiPart := GetHexLineByte ;
     push  psw                  ;
     call  GetHexLineByte       ; LoPart := GetHexLineByte ;
     mov   e , a                ; GetHexLineWord := HiPart << 8 + LoPart ;
     pop   psw                  ;
     mov   d , a                ;
     ret                        ;end (* GetHexLineWord *) ;

Po uruchomieniu kitu 8080, należy wybrać polecenie ładowania kodu poprzez naciśnięcie przycisku F1.
ik8080_il62.png

Zostanie wyświetlony komunikat informacyjny. Po jego ukazaniu się należy wysłać via UART plik zawierający kod programu (nie wcześniej, gdyż strumień znaków zostanie zignorowany).
ik8080_il63.png

Do wysłania pliku użyłem trochę leciwego programu (ale ma możliwość prostego wysłania całego pliku).
ik8080_il64.png

Należy wybrać klawisz Send File i następnie wskazać plik do wysłania.
ik8080_il65.png

Jeżeli program zestawu 8080 nie zgłosi błędu (błąd formatu), to można go uruchomić naciskając przycisk F5/GO, jest komunikat na LCD.
ik8080_il66.png

Uruchomiony program działa, gdyż robi to co było w planie: robi demko z dwoma diodami LED.
ik8080_il67.png

To pozwala z ekscytacją wnieść kilka udogodnień i rozwijać funkcjonalność.
Nie masz wymaganych uprawnień, aby zobaczyć pliki załączone do tego posta.

Prawdziwe słowa nie są przyjemne. Przyjemne słowa nie są prawdziwe.
Lao Tse

Awatar użytkownika
gaweł
Geek
Geek
Posty: 1350
Rejestracja: wtorek 24 sty 2017, 22:05
Lokalizacja: Białystok

Re: Intel 8080 kit

Postautor: gaweł » wtorek 03 lis 2026, 14:05

ik8080_il71.png


Zakończenie i pauza programu

W trakcie ładowania programu poprzez UART, program wychwytuje jego adres początkowy w pamięci RAM. Nie ma obowiązku, by program zaczynał się w lokacji 8000 hex, może być umieszczony dalej w przestrzeni adresowej. W pliku intel-hex znajduje się informacja, pod jaki adres jest ładowany. Ten punkt wejścia do ładowanego programu jest zapamiętany. Polecenie uruchomienia programu wykorzystuje tą informację.
Uruchomiony program sobie działa, robi co ma zamierzone i ma możliwość zakończenia swojej pracy (by załadować i uruchomić kolejny). Do realizacji tych działań zostały wymyślone odpowiednie ekstrakody. Opierają się one o instrukcję RST 1, gdzie w rejestrze B należy umieścić kod operacji oraz w C dane liczbowe, które zostaną pokazane na wyświetlaczu, co pozwoli na identyfikację miejsca zakończenia lub zatrzymania. Szczegóły są następujące:

Kod: Zaznacz cały

;
; Statusy operacji w A
SuccessEC               .equ       0
ErrorEC                 .equ       1
;
; Funkcje zwiazane z zakonczeniem programu bazuja na instrucji RST 1
;
; B - zawiera polecenie:
ExitProgramEC           .equ       1
PasueProgramEC          .equ       2
;
; C - zawiera komunikat konczacy jako liczba do wyswietlenia (na LCD)
;
;

Teraz tworzony jest program do uruchomienia, który robi jeden cykl demka na diodach LED i się zatrzymuje (robi ekstrakod z operacją zatrzymania programu). Tekst programu to:

Kod: Zaznacz cały

Improved compiler for INTEL 8080/8085 processor, version 2.4 licence: CC BY 3.0

  L.NO MEMO    CODE         SRC

Open file: D:\prace\i8080kit\I8080test_0.asm
( . . . )
                
   22.                        .i8080
   23.                  .include "\prace\i8080kit\i8080api.asm"

Open file: \prace\i8080kit\i8080api.asm
( . . . )
   22.                 ;
   23.                 ; Statusy operacji w A
   24. 0000            SuccessEC               .equ       0
   25. 0001            ErrorEC                 .equ       1
   26.                 ;
   27.                 ; Funkcje zwiazane z zakonczeniem programu bazuja na instrucji RST 1
   28.                 ;
   29.                 ; B - zawiera polecenie:
   30. 0001            ExitProgramEC           .equ       1
   31. 0002            PasueProgramEC          .equ       2
   32.                 ; C - zawiera komunikat jako liczba do wyswietlenia konczacy (wyswietlany na LCD)
   33.                 ;
 Close file: \prace\i8080kit\i8080api.asm


Current file: D:\prace\i8080kit\I8080test_0.asm
   24.                 ;
   25. 8000            ResetEntry              .equ    08000h     ;
   26. FFFE            Stack_top               .equ   0fffeh     ;
   27. 00A8            LEDRegister             .equ    0A8h       ;
   28.                 ;*******************************************
   29.                        .org    ResetEntry                  ;
   30. 8000 C33980            jmp     Main                        ;
   31. 8003 03         DemoTable               .defb   00000011b  ;
   32. 8004 06                                 .defb   00000110b  ;
   33. 8005 0C                                 .defb   00001100b  ;
   34. 8006 18                                 .defb   00011000b  ;
   35. 8007 30                                 .defb   00110000b  ;
   36. 8008 60                                 .defb   01100000b  ;
   37. 8009 80                                 .defb   10000000b  ;
   38. 800A 60                                 .defb   01100000b  ;
   39. 800B 30                                 .defb   00110000b  ;
   40. 800C 18                                 .defb   00011000b  ;
   41. 800D 0C                                 .defb   00001100b  ;
   42. 800E 06                                 .defb   00000110b  ;
   43. 800F 03                                 .defb   00000011b  ;
   44. 8010 00                                 .defb   00000000b  ;
   45.                 ;*******************************************
   46. 8011            LEDDemo                                    ;procedure LEDDemo ;
   47.                                                            ;var
   48.                                                            ; TmpPtr : word[hl] ;
   49.                                                            ; TmpCh : byte ;
   50.                                                            ;begin (* LEDDemo *)
   51. 8011 210380            lxi     h , DemoTable               ; TmpPtr := DemoTable ;
   52. 8014            Ledde_3                                    ; repeat
   53. 8014 7E                mov     a , m                       ;  TmpCh := TmpPtr ^ ;
   54. 8015 FE00              cpi     0                           ;  if TmpCh <> 0 then
   55. 8017 CA2580            jz      Ledde_1                     ;  begin
   56. 801A D3A8              out     LEDRegister                 ;   LEDRegister := TmpCh ;
   57. 801C E5                push    h                           ;
   58. 801D CD2F80            call    DemoDelay                   ;   DemoDelay ;
   59. 8020 E1                pop     h                           ;
   60. 8021 23                inx     h                           ;   TmpPtr := TmpPtr  + 1 ;
   61. 8022 C31480            jmp     Ledde_3                     ;  end
   62. 8025            Ledde_1                                    ;  else
   63. 8025 C9                ret                                 ;   exit ;
   64. 8026 C31480            jmp     Ledde_3                     ; until false ;
   65.                                                            ;end (* LEDDemo *) ;
   66.                 ;*******************************************
   67. 8029            Pause                                      ;procedure Pause ;
   68.                                                            ;begin (* Pause *)
   69. 8029 0602              mvi     b , PasueProgramEC          ;
   70. 802B 0E01              mvi     c , 1                       ;
   71. 802D CF                rst     1                           ;
   72. 802E C9                ret                                 ;end (* Pause *) ;
   73.                 ;*******************************************
   74. 802F            DemoDelay                                  ;procedure DemoDelay ;
   75.                                                            ;begin (* DemoDelay *)
   76. 802F 010030            lxi     b , 03000h                  ;
   77. 8032            DeDel_0                                    ;
   78. 8032 0B                dcx     b                           ;
   79. 8033 78                mov     a , b                       ;
   80. 8034 B1                ora     c                           ;
   81. 8035 C23280            jnz     DeDel_0                     ;
   82. 8038 C9                ret                                 ;end (* DemoDelay *) ;
   83.                 ;*******************************************
   84. 8039            Main                                       ;begin (* main *)
   85. 8039 31FEFF            lxi     sp , Stack_top              ;
   86. 803C            Main_0                                     ; repeat
   87. 803C CD1180            call    LEDDemo                     ;  LEDDemo ;
   88. 803F CD2980            call    Pause                       ;  Pause ;
   89. 8042 C33C80            jmp     Main_0                      ; until false ;       
   90.                                                            ;end  (* main *).
   91.                 ;*******************************************
   92.                         .end
Close file: D:\prace\i8080kit\I8080test_0.asm

Compilation :successful

Tu warto zwrócić uwagę na funkcję Pause, która realizuje ekstrakod do systemu, którego zadaniem jest zatrzymanie wykonywania programu użytkowego wraz z wyświetleniem odpowiedniego komunikatu.
ik8080_il72.png

Wykonanie instrukcji RST 1 odkłada na stos informację o adresie powrotu (jak przykładowo instrukcja call), co pozwala później na wznowienie programu od miejsca zatrzymania.
Z każdym uruchamianym programem wiąże się pojęcie stosu. Program użytkowy może ustawić swój własny wskaźnik stosu. W chwili jego pierwszego uruchomienia (po załadowaniu do pamięci RAM), stos jest ustawiony na koniec fizycznej pamięci RAM, ale można go zmienić. Uruchomienie programu wiąże się z odłączeniem od stosu „systemowego”. Realizacja ekstrakodu zatrzymującego program przełącza stos na „systemowy” z zapamiętaniem stosu użytkowego.
Załadowanie programu do RAM wiąże się z zapamiętaniem dwóch informacji: adresu wejścia do programu (pochodzi z pliku intel-hex) oraz wskaźnika stosu użytkownika (z założenia koniec pamięci RAM).

Kod: Zaznacz cały

CommandExecute                   ;procedure CommandExecute ( Ch[a] : byte ) ;
                                 ;begin (* CommandExecute *)
                                 ; case Ch of
   cpi   F1Ch                    ;  F1Ch :
( . . . )
SelCmmEx_4                       ;  F5Ch :
   cpi   F5Ch                    ;
   jnz   SelCmmEx_5              ;
   call  ClrScrLCD               ;   ClrScrLCD ;
                                 ;   if SystemInstance . LoadedStatus then
   lda   SystemInstance + SystemInstanceT . LoadedStatus
   ora   a                       ;
   jnz   SelCmmEx_41             ;   begin
   lxi   h , ProgramNotPresent   ;    WriteTextLCD ( ProgramNotPresent ) ;
   call  WriteTextLCD            ;
   call  NewLineLCD              ;    NewLineLCD ;
   lxi   h , PressAnyKey         ;    WriteTextLCD ( PressAnyKey ) ;
   call  WriteTextLCD            ;
   call  AnyKey                  ;    AnyKey ;
   ret                           ;    exit ;
SelCmmEx_41                      ;   end ;
   lxi   h , ProgramRunning      ;   WriteTextLCD ( ProgramRunning ) ;
   call  WriteTextLCD            ;
   lxi   h , SelCmmEx_0          ;   SystemInstance . SysContinueAdr := SelCmmEx_0 ;
   shld  SystemInstance + SystemInstanceT . SysContinueAdr
   lxi   h , 0                   ;   SystemInstance . SysSP := SP ;
   dad   sp                      ;
   shld  SystemInstance + SystemInstanceT . SysSP
                                 ;   SP := SystemInstance . UserSP ;
   lhld  SystemInstance + SystemInstanceT . UserSP
   sphl                          ;
                                 ;   SystemInstanceT . ProgEntry ^ ( ) ;
   lhld  SystemInstance + SystemInstanceT . ProgEntry
   pchl                          ;
                                 ;   exit ;
SelCmmEx_5                       ;  F6Ch :
( . . . )
                                 ; end (* case *) ;
SelCmmEx_0                       ;
   ret                           ;end (* CommandExecute *) ;

Istotną operacją jest jeszcze zapisanie adresu do kontynuowania działania. Program użytkowy jest uruchamiany instrukcją PCHL (skok do miejsca o adresie zapisanym w parze rejestrów HL bez wskazania adresu powrotu). Wykonanie z programu użytkowego instrukcji RST 1 powinno (w sensie systemu nadzorującego) trafić za operacje uruchamiającą program użytkowy, a właściwie na instrukcję wyjścia z funkcji, która wykonała uruchomienie programu użytkowego (by stos się zgodził co do długości). Realizuje to (SelCmmEx_0 jest etykietą przed instrukcją RET).

Kod: Zaznacz cały

  lxi   h , SelCmmEx_0          ;   SystemInstance . SysContinueAdr := SelCmmEx_0 ;
   shld  SystemInstance + SystemInstanceT . SysContinueAdr

Obsługa instrukcji RST 1 trafia do procedury:

Kod: Zaznacz cały

SysInt1Service                   ;procedure SysInt1Service ;
                                 ; program exit and pause extracode
                                 ;   b = option
                                 ;   c = param
                                 ;begin (* SysInt1Service *)
   mov   a , b                   ;
   cpi   ExitProgramEC           ; if option = ExitProgramEC then
   jnz   SysInt1Srv_1            ; begin
   di                            ;  DisableInt ;
                                 ;  SP := SystemInstance . SysSP ;
   lhld  SystemInstance + SystemInstanceT . SysSP
   sphl                          ;
                                 ;  PushOnStack ( SystemInstance . SysContinueAdr ) ;
   lhld  SystemInstance + SystemInstanceT . SysContinueAdr
   push  h                       ;
   ei                            ;  EnableInt ;
   push  b                       ;
   call  ClrScrLCD               ;  ClrScrLCD ;
   lxi   h , ProgramExitMessage  ;  WriteTextLCD ( ProgramExitMessage ) ;
   call  WriteTextLCD            ;
   call  NewLineLCD              ;  NewLineLCD ;
   lxi   h , ExitMessageCode     ;  WriteTextLCD ( ExitMessageCode ) ;
   call  WriteTextLCD            ;
   pop   b                       ;
   mov   a , c                   ;  HexCnvByteToStr ( param ) ;
   call  HexCnvByteToStr         ;
                                 ;  WriteTextLCD ( SystemInstance . Number ) ;
   lxi   h , SystemInstance + SystemInstanceT . Number
   call  WriteTextLCD            ;
   mvi   a , 0                   ;  SystemInstance . LoadedStatus := false ;
   sta   SystemInstance + SystemInstanceT.LoadedStatus
   ret                           ;  exit -> SystemInstance . SysContinueAdr ;
SysInt1Srv_1                     ; end ;
   cpi   PasueProgramEC          ; if option = PasueProgramEC then
   jnz   SysInt1Srv_2            ; begin
   di                            ;  DisableInt ;
   pop   h                       ;  SystemInstance . ProgEntry := PopFromStack ;
   shld  SystemInstance + SystemInstanceT . ProgEntry
   mvi   a , 1                   ;  SystemInstance . ProgEntryStatus := true ;
   sta   SystemInstance + SystemInstanceT . ProgEntryStatus
   lxi   h , 0                   ;  SystemInstance . UserSP := SP ;
   dad   sp                      ;
   shld  SystemInstance + SystemInstanceT . UserSP
                                 ;  SP := SystemInstance . SysSP ;
   lhld  SystemInstance + SystemInstanceT . SysSP
   sphl                          ;
                                 ;  PushOnStack ( SystemInstance . SysContinueAdr ) ;
   lhld  SystemInstance + SystemInstanceT . SysContinueAdr
   push  h                       ;
   ei                            ;  EnableInt ;
   push  b                       ;
   call  ClrScrLCD               ;  ClrScrLCD ;
   lxi   h , ProgramPauseMessage ;  WriteTextLCD ( ProgramPauseMessage ) ;
   call  WriteTextLCD            ;
   call  NewLineLCD              ;  NewLineLCD ;
   lxi   h , ExitMessageCode     ;  WriteTextLCD ( ExitMessageCode ) ;
   call  WriteTextLCD            ;
   pop   b                       ;
   mov   a , c                   ;  HexCnvByteToStr ( param ) ;
   call  HexCnvByteToStr         ;
                                 ;  WriteTextLCD ( SystemInstance . Number ) ;
   lxi   h , SystemInstance + SystemInstanceT . Number
   call  WriteTextLCD            ;
   ret                           ;  exit -> SystemInstance . SysContinueAdr ;
SysInt1Srv_2                     ; end ;     
   ret                           ;end (* SysInt1Service *) ;

W operacji zakończenia programu wykonane są jedynie operacje związane z ustawieniem stosu na systemowy oraz ustawiona flaga, że program użytkowy nie jest załadowany do RAM. Powrót do „normalności” to umieszczenie na stosie informacji zapisanych w zmiennej SystemInstance . SysContinueAdr (jako adres wyjścia z procedury uruchamia programu użytkowego) oraz następnie instrukcja RET (trafi ponownie do funkcji realizującej polecenia).

Kod: Zaznacz cały

                                ;  PushOnStack ( SystemInstance . SysContinueAdr ) ;
   lhld  SystemInstance + SystemInstanceT . SysContinueAdr
   push  h                       ;
( . . . )
   ret                           ;  exit -> SystemInstance . SysContinueAdr ;

Bardziej złożoną operacją jest zatrzymanie programu z możliwością jego dalszego wznowienia. W tym przypadku należy pobrać ze stosu adres powrotu (miejsce w programie użytkowym za instrukcją RST 1), który zostaje zapisany jako nowy punkt wejścia do programu użytkowego (ewentualnie kolejne GO wznowi program użytkowy). Kolejną ważna operacją jest zapamiętanie aktualnego wskaźnika stosu (programu użytkowego) i zapisanie tych danych. To daje istotne informacje by wznosić program. Po przełączeniu stosu na „systemowy”, następuje powrót do interpretacji poleceń z klawiatury (identycznie jak w operacji zakończenia programu).

Kod: Zaznacz cały

   di                            ;  DisableInt ;
   pop   h                       ;  SystemInstance . ProgEntry := PopFromStack ;
   shld  SystemInstance + SystemInstanceT . ProgEntry
   mvi   a , 1                   ;  SystemInstance . ProgEntryStatus := true ;
   sta   SystemInstance + SystemInstanceT . ProgEntryStatus
   lxi   h , 0                   ;  SystemInstance . UserSP := SP ;
   dad   sp                      ;
   shld  SystemInstance + SystemInstanceT . UserSP
                                 ;  SP := SystemInstance . SysSP ;
   lhld  SystemInstance + SystemInstanceT . SysSP
   sphl                          ;
                                 ;  PushOnStack ( SystemInstance . SysContinueAdr ) ;
   lhld  SystemInstance + SystemInstanceT . SysContinueAdr
   push  h                       ;
   ei                            ;  EnableInt ;
   push  b                       ;
   call  ClrScrLCD               ;  ClrScrLCD ;
   lxi   h , ProgramPauseMessage ;  WriteTextLCD ( ProgramPauseMessage ) ;
   call  WriteTextLCD            ;
   call  NewLineLCD              ;  NewLineLCD ;
   lxi   h , ExitMessageCode     ;  WriteTextLCD ( ExitMessageCode ) ;
   call  WriteTextLCD            ;
   pop   b                       ;
   mov   a , c                   ;  HexCnvByteToStr ( param ) ;
   call  HexCnvByteToStr         ;
                                 ;  WriteTextLCD ( SystemInstance . Number ) ;
   lxi   h , SystemInstance + SystemInstanceT . Number
   call  WriteTextLCD            ;
   ret                           ;  exit -> SystemInstance . SysContinueAdr ;

Wszystko działa:
ik8080_il73.png

Ładowanie programu użytkowego:
ik8080_il74.png


ik8080_il75.png

Uruchomienie programu: program robi ledowe demko i ekstrakod zatrzymania:
ik8080_il76.png

Można go wznowić, robi demko na ledach i ponownie się zatrzymuje (i tak w kółko).
Nie masz wymaganych uprawnień, aby zobaczyć pliki załączone do tego posta.

Prawdziwe słowa nie są przyjemne. Przyjemne słowa nie są prawdziwe.
Lao Tse

Awatar użytkownika
gaweł
Geek
Geek
Posty: 1350
Rejestracja: wtorek 24 sty 2017, 22:05
Lokalizacja: Białystok

Re: Intel 8080 kit

Postautor: gaweł » czwartek 05 lis 2026, 21:35

ik8080_il81.png


Ekstrakody do obsługi wyświetlacza oraz klawiatury


Kolejna rozbudowa dotyczy interfejsu do urządzeń wejścia/wyjścia. W oprogramowanie jest wbudowana obsługa wyświetlacza LCD oraz klawiatury. Nie jest uzasadnione, by te działania powielać w programach użytkowych, najprościej wykorzystać już istniejące. Ta prosta koncepcja niesie jeden problem: adresy funkcji do ich obsługi. Rozwiązanie jest wrażliwe na rzeczywiste położenie ich w przestrzeni adresowej. Niby można wynotować aktualne lokacje wybranych funkcji, ale ten stan ma cechę chwilowości, wystarczy drobne zmiana w programie i cała masa funkcji zmieni swoje położenie z przestrzeni adresowej.
Wyjściem z tego jest stworzenie rozwiązania, które jest w swej koncepcji bezadresowe. Tu można posłużyć się instrukcją RST n. Określając w rejestrach odpowiednią funkcję i wykonując instrukcję RST (ta zawsze ma ściśle określone i stałe położenie w przestrzeni adresowej). Jej obsługa w dalszej części może być już rozwiązana w dowolny sposób. Z punku widzenia programu użytkowego nie musi być znana lokalizacja poszczególnych funkcji, które realizuję właściwe działania, natomiast efekt jest taki, że funkcje działają.
Obecna postać pliku opisująca API do środowiska procka 8080 została poszerzona o stałe parametrów do instrukcji RST (sam kilka razy podałem nie ten numer i wyszło, jak wyszło – nie zawsze do czego chciałem).

Kod: Zaznacz cały

;
; C - zawiera komunikat kończący jako liczba do wyświetlenia (na LCD)
;
;****************************************************************************
;
; Funkcje związane z obsługą wyświetlacza LCD bazują na instrukcji RST 2
ScreenControlEC         .equ       2
;
; B - zawiera polecenie:
WriteChEC               .equ       1   ; w C jest znak
WriteStringEC           .equ       2   ; w HL jest adres
ClrScrEC                .equ       3
HomeEC                  .equ       4
NewLineEC               .equ       5
SetCursorOnEC           .equ       6   ; w C jest BlinkMode (boolean)
SetCursonOffEC          .equ       7
SetCursorPosEC          .equ       8   ; w C jest pozycja bity 5..0 – kolumna
                                       ; bity 7..6 - wiersz
Write8BitHexEC          .equ       9   ; w C jest liczba
Write16BitHexEC         .equ      10   ; w HL jest liczba
Write8BitBinaryEC       .equ      11   ; w C jest liczba
Write16BitBinaryEC      .equ      12   ; w HL jest liczba
Write8BitDecimalEC      .equ      13   ; w C jest liczba
Write16BitDecimalEC     .equ      14   ; w HL jest liczba
Write8BitDecimalU2EC    .equ      15   ; w C jest liczba
Write16BitDecimalU2EC   .equ      16   ; w HL jest liczba
;

W przypadku obsługi LCD są to funkcje do pisania po jednym znaku oraz wypisania ciągu znaków, typowych operacji jak czyszczenie ekranu, zmiana wiersza, sterowanie kursorem. Do tego kompletu dodałem często używane operacje dotyczące pisania po ekranie liczb, które wymagają konwersji z postaci binarnej na znakową. Istnieje możliwość obsługi liczb 8-bitowych oraz 16-bitowych z formatowaniem do postaci binarnej (zero/jeden), do postaci szesnastkowej, dziesiętnej (jako liczb bez znaku) oraz do postaci dziesiętnej jako liczby ze znakiem. Przykładowy program użytkowy wygląda następująco:

Kod: Zaznacz cały

       .i8080
.include "\prace\i8080kit\i8080api.asm"
;
ResetEntry           .equ    08000h  ;
Stack_top            .equ    0fffeh  ;
;****************************************
    .org    ResetEntry               ;
    jmp     Main                     ;
HelloText   .defb    "Hello, on LCD" , 0
;****************************************
Delay                                ;procedure Delay ;
                                     ;begin (* Delay *)
    lxi     b , 0h                   ;
Del_0                                ;
    dcx     b                        ;
    mov     a , b                    ;
    ora     c                        ;
    jnz     Del_0                    ;
    ret                              ;end (* Delay *) ;
;*************************************
DemoDelay                            ;procedure DemoDelay ;
                                     ;begin (* DemoDelay *)
    call    Delay                    ;
    call    Delay                    ;
    call    Delay                    ;
    call    Delay                    ;
    call    Delay                    ;
    ret                              ;end (* DemoDelay *) ;
;*************************************
Main                                 ;begin (* main *)
    lxi     sp , Stack_top           ;
    mvi     b , ClrScrEC             ; ScrScrLCD ;
    rst     ScreenControlEC          ;
    mvi     b , WriteStringEC        ; WriteString ( HelloText ) ;
    lxi     h , HelloText            ;
    rst     ScreenControlEC          ;
    call    DemoDelay                ; DemoDelay ;
    mvi     b , ClrScrEC             ; ScrScrLCD ;
    rst     ScreenControlEC          ;
    mvi     b , Write8BitHexEC       ; Write8BitHex ( 12h ) ;        12
    mvi     c , 12h                  ;
    rst     ScreenControlEC          ;
    call    DemoDelay                ; DemoDelay ;
    mvi     b , ClrScrEC             ; ScrScrLCD ;
    rst     ScreenControlEC          ;
    mvi     b , Write8BitHexEC       ; Write8BitHex ( 0AAh ) ;       AA
    mvi     c , 0AAh                 ;
    rst     ScreenControlEC          ;
    call    DemoDelay                ; DemoDelay ;
    mvi     b , ClrScrEC             ; ScrScrLCD ;
    rst     ScreenControlEC          ;
    mvi     b , Write8BitHexEC       ; Write8BitHex ( 055h ) ;       55
    mvi     c , 055h                 ;
    rst     ScreenControlEC          ;
    call    DemoDelay                ; DemoDelay ;
    mvi     b , ClrScrEC             ; ScrScrLCD ;
    rst     ScreenControlEC          ;
    mvi     b , Write16BitHexEC      ; Write16BitHex ( 01234h ) ;    1234
    lxi     h , 01234h               ;
    rst     ScreenControlEC          ;
    call    DemoDelay                ; DemoDelay ;
    mvi     b , ClrScrEC             ; ScrScrLCD ;
    rst     ScreenControlEC          ;
    mvi     b , Write16BitHexEC      ; Write16BitHex ( 06789h ) ;    6789
    lxi     h , 06789h               ;
    rst     ScreenControlEC          ;
    call    DemoDelay                ; DemoDelay ;
    mvi     b , ClrScrEC             ; ScrScrLCD ;
    rst     ScreenControlEC          ;
    mvi     b , Write16BitHexEC      ; Write16BitHex ( 0ABCDh ) ;    ABCD
    lxi     h , 0ABCDh               ;
    rst     ScreenControlEC          ;
    call    DemoDelay                ; DemoDelay ;
    mvi     b , ClrScrEC             ; ScrScrLCD ;
    rst     ScreenControlEC          ;
    mvi     b , Write8BitDecimalEC   ; Write8BitDecimal ( 0ABh ) ;   171
    mvi     c , 0ABh                 ;
    rst     ScreenControlEC          ;
    call    DemoDelay                ; DemoDelay ;
    mvi     b , ClrScrEC             ; ScrScrLCD ;
    rst     ScreenControlEC          ;
    mvi     b , Write8BitDecimalEC   ; Write8BitDecimal ( 067h ) ;   103
    mvi     c , 067h                 ;
    rst     ScreenControlEC          ;
    call    DemoDelay                ; DemoDelay ;
    mvi     b , ClrScrEC             ; ScrScrLCD ;
    rst     ScreenControlEC          ;
    mvi     b , Write16BitDecimalEC  ; Write16BitDecimal ( 01267h ) ; 4711
    lxi     h , 01267h               ;
    rst     ScreenControlEC          ;
    call    DemoDelay                ; DemoDelay ;
    mvi     b , ClrScrEC             ; ScrScrLCD ;
    rst     ScreenControlEC          ;
    mvi     b , Write16BitDecimalEC  ; Write16BitDecimal ( 0ABCDh ) ; 43981
    lxi     h , 0ABCDh               ;
    rst     ScreenControlEC          ;
    call    DemoDelay                ; DemoDelay ;
    mvi     b , ClrScrEC             ; ScrScrLCD ;
    rst     ScreenControlEC          ;
    mvi     b , Write8BitDecimalU2EC ; Write8BitDecimalU2 ( 0ABh ) ;  -85
    mvi     c , 0ABh                 ;
    rst     ScreenControlEC          ;
    call    DemoDelay                ; DemoDelay ;
    mvi     b , ClrScrEC             ; ScrScrLCD ;
    rst     ScreenControlEC          ;
    mvi     b , Write8BitDecimalU2EC ; Write8BitDecimalU2 ( 067h ) ;   103
    mvi     c , 067h                 ;
    rst     ScreenControlEC          ;
    call    DemoDelay                ; DemoDelay ;
    mvi     b , ClrScrEC             ; ScrScrLCD ;
    rst     ScreenControlEC          ;
    mvi     b , Write16BitDecimalU2EC; Write16BitDecimalU2 ( 01267h ) ; 4711
    lxi     h , 01267h               ;
    rst     ScreenControlEC          ;
    call    DemoDelay                ; DemoDelay ;
    mvi     b , ClrScrEC             ; ScrScrLCD ;
    rst     ScreenControlEC          ;
    mvi     b , Write16BitDecimalU2EC; Write16BitDecimalU2 ( 0ABCDh ) ; -21555
    lxi     h , 0ABCDh               ;
    rst     ScreenControlEC          ;
    call    DemoDelay                ; DemoDelay ;
    mvi     b , ClrScrEC             ; ScrScrLCD ;
    rst     2                        ;
    mvi     b , Write8BitBinaryEC    ; Write8BitBinary ( 0ABh ) ;
    mvi     c , 0ABh                 ;
    rst     ScreenControlEC          ;
    call    DemoDelay                ; DemoDelay ;
    mvi     b , ClrScrEC             ; ScrScrLCD ;
    rst     ScreenControlEC          ;
    mvi     b , Write8BitBinaryEC    ; Write8BitBinary ( 05Ah ) ;
    mvi     c , 05Ah                 ;
    rst     ScreenControlEC          ;
    call    DemoDelay                ; DemoDelay ;
    mvi     b , ClrScrEC             ; ScrScrLCD ;
    rst     ScreenControlEC          ;
    mvi     b , Write16BitBinaryEC   ; Write16BitBinary ( 0C33Ch ) ;
    lxi     h , 0C33Ch               ;
    rst     ScreenControlEC          ;
    call    DemoDelay                ; DemoDelay ;
    mvi     b , ClrScrEC             ; ScrScrLCD ;
    rst     ScreenControlEC          ;
    mvi     b , ExitProgramEC        ; ProgramTerminate ;
    mvi     c , 0                    ;
    rst     ProgControlEC            ;
                                     ;end  (* main *).
;*************************************
    .end

Po załadowaniu i uruchomieniu programu, kilka wybranych fotek:
ik8080_il82.png


ik8080_il83.png


ik8080_il84.png


ik8080_il85.png


ik8080_il86.png


ik8080_il87.png


ik8080_il88.png


ik8080_il89.png


ik8080_il810.png

W podobnej technologii rozwiązany jest interfejs do klawiatury. Tu są dostępne operacje pozwalające stwierdzić, czy w kolejce FIFO klawiatury znajduje się znak oraz pozwala wczytać ten znak. Dodatkową funkcję jest usunięcie wszystkich znaków z kolejki FIFO.
Fragment pliku API dotyczący klawiatury to (oprócz identyfikatorów operacji jest zestaw znaków generowanych przez klawiaturę):

Kod: Zaznacz cały

; Funkcje związane klawiaturą bazują na instrukcji RST 3
;
KeybControlEC           .equ       3
;
; B - zawiera polecenie:
KeyPressedEC            .equ       1   ; odpowiedź: w C jest flaga: 0/nie 0
GetKeyEC                .equ       2   ; odpowiedź: w C jest kod znaku
ClearKeybFifoEC         .equ       3   ; brak odpowiedzi
;
Digit0Ch                .equ       '0'    ;
Digit1Ch                .equ       '1'    ;
Digit2Ch                .equ       '2'    ;
Digit3Ch                .equ       '3'    ;
Digit4Ch                .equ       '4'    ;
Digit5Ch                .equ       '5'    ;
Digit6Ch                .equ       '6'    ;
Digit7Ch                .equ       '7'    ;
Digit8Ch                .equ       '8'    ;
Digit9Ch                .equ       '9'    ;
DigitACh                .equ       'A'    ;
DigitBCh                .equ       'B'    ;
DigitCCh                .equ       'C'    ;
DigitDCh                .equ       'D'    ;
DigitECh                .equ       'E'    ;
DigitFCh                .equ       'F'    ;
F1Ch                    .equ       'a'    ;
F2Ch                    .equ       'b'    ;
F3Ch                    .equ       'c'    ;
F4Ch                    .equ       'd'    ;
F5Ch                    .equ       'e'    ;
F6Ch                    .equ       'f'    ;
F7Ch                    .equ       'g'    ;
F8Ch                    .equ       'h'    ;
;

Przykładowy program do badania klawiatury jest następujący (gdzie warto zwrócić uwagę na punkt wejścia do programu wynoszący 8100 hex).

Kod: Zaznacz cały

       .i8080
.include "\prace\i8080kit\i8080api.asm"
;
ResetEntry              .equ    08100h     ;
Stack_top               .equ    0fffeh     ;
;
EscCh                   .equ        F8Ch   ;
;*******************************************
       .org    ResetEntry                  ;
       jmp     Main                        ;
HelloText      .defb    "Hello, on LCD" , 0
;*******************************************
Main                                       ;begin (* main *)
       lxi     sp , Stack_top              ;
       mvi     b , ClrScrEC                ; ScrScrLCD ;
       rst     ScreenControlEC             ;
       mvi     b , WriteStringEC           ; WriteString ( HelloText ) ;
       lxi     h , HelloText               ;
       rst     ScreenControlEC             ;
       mvi     b , NewLineEC               ; NewLine ;
       rst     ScreenControlEC             ;
Main_0                                     ; repeat
       mvi     b , KeyPressedEC            ;  if KeyPressed then
       rst     KeybControlEC               ;
       mov     a , c                       ;
       ora     a                           ;
       jz      Main_0                      ;  begin
       mvi     b , GetKeyEC                ;   Tmp := ReadKey ;
       rst     KeybControlEC               ;
       push    b                           ;
       mvi     b , WriteChEC               ;   WriteChLCD ( Tmp ) ;
       rst     ScreenControlEC             ;
       pop     b                           ;   if Tmp = EscCh then
       mov     a , c                       ;
       cpi     EscCh                       ;
       jnz     Main_0                      ;
       mvi     b , ExitProgramEC           ;    ProgramTerminate ;
       mvi     c , 3ch                     ;
       rst     ProgControlEC               ;
                                           ; until false ;
                                           ;end  (* main *).
;*******************************************
       .end

Program wyświetla znaki uzyskane z klawiatury oraz kończy pracę po naciśnięcie klawisza F8/ESC (jako komunikat kończący program występuje liczba 3C hex). Po załadowaniu programu występuje komunikat mówiący o punkcie wejścia do programu.
ik8080_il811.png

Uruchomienie daje efekt „maszyny do pisania”.
ik8080_il812.png

Naciśnięcie ESC kończy działanie programu z wymaganym komunikatem.
ik8080_il813.png
Nie masz wymaganych uprawnień, aby zobaczyć pliki załączone do tego posta.

Prawdziwe słowa nie są przyjemne. Przyjemne słowa nie są prawdziwe.
Lao Tse

Awatar użytkownika
j23
Expert
Expert
Posty: 540
Rejestracja: czwartek 08 paź 2015, 18:40

Re: Intel 8080 kit

Postautor: j23 » piątek 06 lis 2026, 15:42

Dziękuję za kolejną, cenną porcję informacji Kolego Gaweł. Oglądając te listingi kodu Assemblera i pseudokodu przypomniało mi się takie stare powiedzenie (chyba z jednej -takiej starej- dokumentacji IBM'owskiej): "Linia komentarza do linii kodu". Aż miło się czyta kod napisany w taki sposób.
:like:

Pozdrawiam! J23
Internet łączy ludzi, którzy dzielą się swoimi zainteresowaniami, pomysłami i potrzebami, bez względu na geograficzne (przeciwności).
BOB TAYLOR, PARC

Awatar użytkownika
photin
Posty: 18
Rejestracja: niedziela 08 paź 2023, 12:43
Lokalizacja: Klaudyn-Pruszkow
Kontaktowanie:

Re: Intel 8080 kit

Postautor: photin » piątek 06 lis 2026, 17:13

Gawel, dziekuje Ci za podzielenie sie Twoim doswiadczeniem w projektowaniu, programowaniu i debugowaniu systemow mikroprocesorowych.
Jak juz bede mial lokalnego AI, to kaze mu sciagnac wszystkie Twoje posty i doradzac mi na ich podstawie ;-)..

Awatar użytkownika
gaweł
Geek
Geek
Posty: 1350
Rejestracja: wtorek 24 sty 2017, 22:05
Lokalizacja: Białystok

Re: Intel 8080 kit

Postautor: gaweł » sobota 07 lis 2026, 15:57

No jest jeszcze parę rzeczy do zrobienia i nad tym pracuję.

Prawdziwe słowa nie są przyjemne. Przyjemne słowa nie są prawdziwe.
Lao Tse

Awatar użytkownika
gaweł
Geek
Geek
Posty: 1350
Rejestracja: wtorek 24 sty 2017, 22:05
Lokalizacja: Białystok

Re: Intel 8080 kit

Postautor: gaweł » niedziela 08 lis 2026, 20:15

ik8080_il901.png


UART w programie użytkowym
ekstrakody transmisji szeregowej


Transmisja szeregowa jest jedną z najprostszych form komunikacji z mikroprocesorem. Skoro w układzie uruchomieniowym jest kontroler pozwalający realizować szeregowe przesyłanie danych, to nie jest uzasadnione, by ładowany program użytkowy nie miał możliwości jego wykorzystania. Chociaż pierwotnie jego przeznaczenie było dedykowane do ładowania kodu (chyba nikt nie ma ochoty wpisywać kodu programu z klawiatury → jest to nieoptymalne i bardzo wrażliwe na pomyłki). Można uznać, że układ 8250 jest współdzielony.
Do obsługi transmisji szeregowej również powstały odpowiednie ekstrakody. W tym przypadku dotyczą transmisji znaku jak również dłuższego ciągu znaków (musi być zakończony znakiem o kodzie 0). W drugą stronę jest możliwość sprawdzenia, czy w kolejkach znajdują się jakiekolwiek znaki do wczytania oraz ich wczytanie. Dodatkowo można wyzerować istniejącą kolejkę FIFO związaną z transmisją szeregową. Analogicznie jak poprzednio, jest zdefiniowanych kilka kolejnych stałych dotyczących API transmisji szeregowej (tym razem opartych o instrukcję RST 4):

Kod: Zaznacz cały

; Funkcje związane z UART bazują na instrukcji RST 4
;
UARTControlEC           .equ       4
;
; B - zawiera polecenie:
SerialDataPresentEC     .equ       1   ; odpowiedź: w C jest flaga: 0/nie 0
GetSerialDataEC         .equ       2   ; odpowiedź: w C jest kod znaku
SentSerialCharEC        .equ       3   ; w C jest znak
SendSerialStringEC      .equ       4   ; w HL jest adres
SerialClearFifoEC       .equ       5   ; bez parametrów

Krótki program diagnostyczno/prezentacyjny jest następujący:

Kod: Zaznacz cały

       .i8080
.include "\prace\i8080kit\i8080api.asm"
;
ResetEntry              .equ    08000h     ;
Stack_top               .equ    0fffeh     ;
;
EscCh                   .equ        F8Ch   ;
;*******************************************
       .org    ResetEntry                  ;
       jmp     Main                        ;
HelloText      .defb    "Hello, on serial" , 0Dh , 0Ah , 0
ProgramExit    .defb    0Dh , 0Ah , "Koniec programu" , 0Dh , 0Ah , 0
;*******************************************
Main                                       ;begin (* main *)
       lxi     sp , Stack_top              ;
       mvi     b , SendSerialStringEC      ; SendSerialString ( HelloText ) ;
       lxi     h , HelloText               ;
       rst     UARTControlEC               ;
Main_0                                     ; repeat
       mvi     b , SerialDataPresentEC     ;  if SerialDataPresent then
       rst     UARTControlEC               ;
       mov     a , c                       ;
       ora     a                           ;
       jz      Main_0                      ;  begin
       mvi     b , GetSerialDataEC         ;   Tmp := GetSerial ;
       rst     UARTControlEC               ;
       mov     a , c                       ;   if Tmp = 1Bh (* ESC *) then
       cpi     1Bh                         ;
       jnz     Main_1                      ;   begin
       mvi     b , SendSerialStringEC      ;    SendSerialString ( ProgramExit ) ;
       lxi     h , ProgramExit             ;
       rst     UARTControlEC               ;
       mvi     b , ExitProgramEC           ;    ProgramTerminate ;
       mvi     c , 0Ch                     ;
       rst     ProgControlEC               ;
                                           ;   end ;
Main_1                                     ;
       mvi     b , SentSerialCharEC        ;   SentSerial ( Tmp ) ;
       rst     UARTControlEC               ;
       jmp     Main_0                      ; until false ;
                                           ;end  (* main *).
;*******************************************
       .end

Ten krótki program wczytuje dane z UART. Każdy wczytany znak jest wysyłany do UART (działa jak maszyna do pisania). Praca programu kończy się po wczytaniu znaku ESC (kod znaku 1B hex).
Po wysłaniu via UART kodu nowego programu, kliknąłem na Clear w okienku nadajnika i odbiornika i uruchomiłem załadowany program (znaki $0D$0A w okienku do wysyłania to znaki CR oraz LF).
ik8080_il902.png
Nie masz wymaganych uprawnień, aby zobaczyć pliki załączone do tego posta.

Prawdziwe słowa nie są przyjemne. Przyjemne słowa nie są prawdziwe.
Lao Tse


Wróć do „Retro”

Kto jest online

Użytkownicy przeglądający to forum: Obecnie na forum nie ma żadnego zarejestrowanego użytkownika i 2 gości