Intel 8080 kit
Re: Intel 8080 kit
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.
Re: Intel 8080 kit
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
X is work. Y is play. Z is keep your mouth shut."A. Einstein
Re: Intel 8080 kit
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).
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 3Kod: 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.
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 *) ;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
Re: Intel 8080 kit
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).
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 .RXDRecTOdbió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:
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
Re: Intel 8080 kit
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.hexZ 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
:00000001FFRealizacja 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.
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).
Do wysłania pliku użyłem trochę leciwego programu (ale ma możliwość prostego wysłania całego pliku).
Należy wybrać klawisz Send File i następnie wskazać plik do wysłania.
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.
Uruchomiony program działa, gdyż robi to co było w planie: robi demko z dwoma diodami LED.
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
Re: Intel 8080 kit
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 :successfulTu 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.
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 . SysContinueAdrObsł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:
Ładowanie programu użytkowego:
Uruchomienie programu: program robi ledowe demko i ekstrakod zatrzymania:
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
Re: Intel 8080 kit
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 *).
;*************************************
.endPo załadowaniu i uruchomieniu programu, kilka wybranych fotek:
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 *).
;*******************************************
.endProgram 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.
Uruchomienie daje efekt „maszyny do pisania”.
Naciśnięcie ESC kończy działanie programu z wymaganym komunikatem.
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
Re: Intel 8080 kit
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.
Pozdrawiam! J23
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
BOB TAYLOR, PARC
- photin
- Posty: 18
- Rejestracja: niedziela 08 paź 2023, 12:43
- Lokalizacja: Klaudyn-Pruszkow
- Kontaktowanie:
Re: Intel 8080 kit
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
..
Jak juz bede mial lokalnego AI, to kaze mu sciagnac wszystkie Twoje posty i doradzac mi na ich podstawie
Re: Intel 8080 kit
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
Re: Intel 8080 kit
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ówKró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 *).
;*******************************************
.endTen 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).
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
Kto jest online
Użytkownicy przeglądający to forum: Obecnie na forum nie ma żadnego zarejestrowanego użytkownika i 2 gości


