= HF8V = / = SP8WJT = / = SP0106RZ =


HAVR




Project Info

HAVR to mój własny język programowania i interpreter tego języka instalowany na różnych małych systemach cyfrowych. Pierwsze próby były prowadzone na systemie HF-6060 (AT90S2313) na początku lat 2000. Miałem wtedy tylko ten jeden procesor i brakowało mi w nim pamięci. Powstał wtedy mikroskopijny system o rozmiarze pamięci programu 64 bajty i dosowy kompilator. System działał, ale miał niewielkie błędy. Następnie pierwszy działający w miarę bezbłędnie system powstał dla systemu HF-6048 a następnie na innych systemach. HAVR jest przydatny zwłaszcza na tych mikroprocesorach które nie mają możliwości wykonywania kodu w RAM.

Istnieją różne podwersje HAVR:

  • HAVR8 - pamięć programu 256 bajtów - najczęściej używany
  • HAVR6 - pamięć programu 64 bajtów - prowadzone są eksperymenty na ATTINY13 - raczej mało praktycznie przydatny
  • HAVR16 - pamięć programu 64KB - prowadzone są eksperymenty na STM32 - potencjalnie ciekawy ale daleko nie tak wydajny jak HAVR8


  • HAVR pozwala na konstruowanie bardzo wydajnych systemów cyfrowych z dużą ilością ustandaryzowanych funkcji wbudowanych i praktycznie nieograniczoną możliwości dodawania aplikacji zewnętrznych napisanych w HAVR. Ta ostatnia możliwość jest bardzo pożyteczna w mikroprocesorach AVR które nie pozwalają na wykonanie własnego kodu w RAM. Największą wadą są narzuty czasowe przebiegu pętli interpretera które wynoszą typowo co najmniej 1.375 us (@ 8MHz). Z tego powodu funkcje "szybkie" należy nadal pisać w języku danego mikroprocesora, natomiast mogą być wywoływane i kontrolowane przez HAVR.

    Od strony sprzętowej minimalny system cyfrowy wykonany z systemem HAVR może mieć tylko buzzer jako urządzenie wyjściowe i kilka klawiszy klawiatury lokalnej lub zewnętrzny UART. Wszystkie komendy z systemu odbiera sie wtedy telegrafią za pomocą kodów HEX lub 3 literowych skrótów a funkcje wywołuje się za pomocą kodu HEX w tabeli Wektorów lub z Menu za pomocą 1-3 literowych skrótów. Obowiązuje wyłącznie format heksadecymalny poza nielicznymi wyjątkami.

    HAVR jest językiem niskiego poziomu ma jednak sporo wbudowanych funkcji wysokiego poziomu, np. instrukcje dotyczące I2C, dlatego że HAVR od swoich początków był używany do programowania urządzeń zewnętrznych jak moduły PLL, RTC, itd...

    W dalszej części nastąpi opis programistyczny standardów HAVR8, jako że inne podwersje nie mają obecnie praktycznego znaczenia.


    Model programistyczny HAVR8

    HAVR8 odznacza się bardzo wysoką wydajnością kodu. Jest to spowodowanhe faktem że 66% instrukcji to instrukcje jednobajtowe a jedyny tryb adresowy to 8-bitowy, absolutny. Najprostszy program może mieć typowo poniżej 10 bajtów (np. oscylator) a pamięć programu 256 bajtów zwykle nie jest cała wykorzystywana. Z uwagi na małą ilość instrukcji i prosty tryb adresowania programy można pisać nawet w kodzie maszynowym HAVR8 na kartce papieru.


    DANE TECHNICZNE HAVR8

    AKTUALNA WERSJA HAVR: 39
    PAMIĘĆ PROGRAMU: 256 B (typowo pierwszy wolny w całości segment RAM, np. w ATMEGA8 0x100-0x1FF)
    ILOŚĆ INSTRUKCJI: 70 (HF-6030), 72 (HF-6048), 80 (HF-6050, HF-6128, HF-6158)
    MINIMALNY CZAS WYKONANIA INSTRUKCJI: 1.375us


    Rejestry HAVR8:

    REGAVR REGAVR ADRREGISTER FUNCTION
    A r16 0x10 rejestr główny, akumulator
    B r17 0x11 dodatkowy rejestr dla wielu instrukcji arytmetycznych i logicznych
    C r18 0x12 licznik dla instrukcji LOOP
    D r19 0x13
    E r20 0x14
    FLAG r0 0x00 przepisywany z SREG po wykonaniu operacji arytmetycznych i bitowych
    PC r26, r27 (XL, XH) 0x1A-0x1B licznik programu HAVR


    Podczas implementacji HAVR8 na innych mikroprocesorach rejestry A-E powinny być rejestrami w których funkcje najczęściej przyjmują i zwracają parametry. Rejestr używany przez PC nie może być używany przez inne funkcje które w sposób jawny bądź niejawny mogą być wywołane przez HAVR.


    Instrukcje HAVR8:

    MNEMONICCODEPARAM.INSTRUCTION DESCRIPTIONREMARKS
    END 0x00 0 Kończy działanie programu HAVR
    LDA 0x01 imm8 1 Ładuje rejestr A konstantą A <- imm8
    LDB 0x02 imm8 1 Ładuje rejestr B konstantą B <- imm8
    LDC 0x03 imm8 1 Ładuje rejestr C konstantą C <- imm8
    LDD 0x04 imm8 1 Ładuje rejestr D konstantą D <- imm8
    LDE 0x05 imm8 1 Ładuje rejestr E konstantą E <- imm8
    PHA 0x06 0 Odkłada rejestr A na stos stack <- A
    PPA 0x07 0 Przywraca rejestr A ze stosu A <- stack
    PHB 0x08 0 Odkłada rejestr B na stos stack <- B
    PPB 0x09 0 Przywraca rejestr B ze stosu B <- stack
    PHC 0x0A 0 Odkłada rejestr C na stos stack <- C
    PPC 0x0B 0 Przywraca rejestr C ze stosu C <- stack
    PHD 0x0C 0 Odkłada rejestr D na stos stack <- D
    PPD 0x0D 0 Przywraca rejestr D ze stosu D <- stack
    PHE 0x0E 0 Odkłada rejestr E na stos stack <- E
    PPE 0x0F 0 Przywraca rejestr E ze stosu E <- stack
    NOP 0x10 0 Brak operacji
    RPA 0x11 0 Odczytuje PORTA do rejestru A [*] A <- PORTA
    RPB 0x12 0 Odczytuje PORTB do rejestru A [*] A <- PORTB
    RPC 0x13 0 Odczytuje PORTC do rejestru A [*] A <- PORTC
    RPD 0x14 0 Odczytuje PORTD do rejestru A [*] A <- PORTD
    WPA 0x15 0 Zapisuje PORTA z rejestru A [*] PORTA <- A
    WPB 0x16 0 Zapisuje PORTB z rejestru A [*] PORTB <- A
    WPC 0x17 0 Zapisuje PORTC z rejestru A [*] PORTC <- A
    WPD 0x18 0 Zapisuje PORTD z rejestru A [*] PORTD <- A
    ADD 0x19 0 Dodaje zawartość rejestru B do rejestru A [^] A <- (A + B) [^]
    SUB 0x1A 0 Odejmuje zawartość rejestru B od rejestru A [^] A <- (A - B) [^]
    AND 0x1B 0 Wykonuje bitowe AND na rejestrze A i rejestrze B [^] A <- (A AND B) [^]
    OR 0x1C 0 Wykonuje bitowe OR na rejestrze A i rejestrze B [^] A <- (A OR B) [^]
    XOR 0x1D 0 Wykonuje bitową różnicę symetryczną na rejestrze A i rejestrze B [^] A <- (A XOR B) [^]
    NEG 0x1E 0 Wykonuje arytmetyczną negację na rejestrze A [^] A <- (NEG A) [^]
    NOT 0x1F 0 Wykonuje bitową negację na rejestrze A [^] A <- (NOT A) [^]
    LSL 0x20 0 Wykonuje przesunięcie bitowe na rejestrze A w lewo [^] A <- (A << 1) [^]
    LSR 0x21 0 Wykonuje przesunięcie bitowe na rejestrze A w prawo [^] A <- (A >> 1) [^]
    DEC 0x22 0 Wykonuje zmniejszenie o 1 rejestru A [^] A <- (A - 1) [^]
    INC 0x23 0 Wykonuje zwiększenie o 1 rejestru A [^] A <- (A + 1) [^]
    INT 0x24 vect8 1 Wywołuje podany wektor przerwania softwarowego PC <- (@INT)
    I2CRD 0x25 0 Odczytuje bajt z magistrali I2C do rejestru A
    I2CIT 0x26 0 Inicjacja magistrali I2C
    I2CST 0x27 imm8 1 Nadaje warunek START na magistrali I2C (należy podać adres I2C)
    I2CWR 0x28 imm8 1 Zapisuje podany bajt do magistrali I2C
    I2CSP 0x29 0 Nadaje warunek STOP na magistrali I2C
    KBD 0x2A 0 Wprowadza znak ASCII z urządzenia wejściowego do rejestru A A <- INPUT_DEVICE
    CPBNE 0x2B imm8 aa8 2 Porównuje zawartość rejestru A z konstantą, skacze pod wskazany adres jeśli nierówne if (A != imm8) PC <- aa8 else PC <- (PC + 3)
    CALL 0x2C aa8 1 Wywołanie funkcji stack <- (PC+2), PC <- aa8
    RET 0x2D 0 Powrot z funkcji PC <- stack
    LOOP 0x2E aa8 1 Pętla z licznikiem w rejestrze C if (C>0) PC <- aa8 else PC <- (PC + 2)
    JMP 0x2F aa8 1 Skok pod 8 bitowy adres HAVR uC PC <- aa16
    GOTO 0x30 aa16h aa16l 2 Skok pod 16 bitowy adres w pamieci programu mikroprocesora (powrót do HAVR niemożliwy) uC PC <- aa16
    DEL20U 0x31 0 Softwarowe opóźnienie 20us
    DEL100U 0x32 0 Softwarowe opóźnienie 100us
    DEL1M 0x33 0 Softwarowe opóźnienie 1ms
    ST0 0x34 aa8 1 Składuje rejestr A pod wskazany adres RAMSEG0 [**] RAMSEG0(aa8) <- A
    DEL10M 0x35 0 Softwarowe opóźnienie 10ms
    DEL50M 0x36 0 Softwarowe opóźnienie 50ms
    DEL100M 0x37 0 Softwarowe opóźnienie 100ms
    DEL200M 0x38 0 Softwarowe opóźnienie 200ms
    DEL500M 0x39 0 Softwarowe opóźnienie 500ms
    DEL1 0x3A 0 Softwarowe opóźnienie 1s
    DBG 0x3B 0 Debug Break - zatrzymuje wykonanie programu i wyświetla: PC, A, B, C, D, E
    WRT 0x3C aa8 1 Zapisuje string do urządzenia wyjściowego spod wskazanego adresu if(@aa8!=0) { @aa8->OUTPUT_DEVICE, aa8++}
    WRS 0x3D imm8 1 Zapisuje kod błędu do urządzenia ERROR DEVICE ERROR_DEVICE <- imm8
    INS 0x3E 0 Wprowadza daną do rejestru A w formacie HEX z urządzenia wejściowego A <- INPUT_DEVICE
    DSP 0x3F 0 Zapisuje do urządzenia wyjściowego daną z rejestru A w formacie HEX OUTPUT_DEVICE <- A
    LD0 0x40 aa8 1 Ładuje rejestr A z RAMSEG0 [**] A <- RAMSEG0(aa8)
    WRI 0x41 aa8 imm8 2 Składuje konstantę pod wskazany adres RAMSEG0 [**] RAMSEG0(aa8) <- imm8
    LD 0x42 aa8 1 Ładuje rejestr A z pamięci programu HAVR (RAMSEG1) A <- RAMSEG1(aa8)
    ST 0x43 aa8 1 Składuje rejestr A do pamięci programu HAVR (RAMSEG1) RAMSEG1(aa8) <- A
    LD2 0x44 aa8 1 Ładuje rejestr A ze wskazanego adresu RAMSEG2 A <- RAMSEG2(aa8)
    ST2 0x45 aa8 1 Składuje rejestr A pod wskazany adres RAMSEG2 RAMSEG2(aa8) <- A
    LD3 0x46 aa8 1 Ładuje rejestr A ze wskazanego adresu RAMSEG3 A <- RAMSEG3(aa8)
    ST3 0x47 aa8 1 Składuje rejestr A pod wskazany adres RAMSEG3 RAMSEG3(aa8) <- A
    CPBLO 0x48 imm8 aa8 2 Porównuje zawartość rejestru A z konstantą, skacze pod wskazany adres jeśli niższa if (A > imm8) PC <- aa8 else PC <- (PC+3)
    RPE 0x49 0 Odczytuje PORTE do rejestru A [***]
    RPF 0x4A 0 Odczytuje PORTF do rejestru A [***]
    WPE 0x4B 0 Zapisuje PORTE z rejestru A [***]
    WPF 0x4C 0 Zapisuje PORTF z rejestru A [***]
    LDX 0x4D aa16h aa16l 2 Ładuje rejestr A z RAM wskazanego przez adres 16 bitowy [***] A <- RAM(aa16)
    STX 0x4E aa16h aa16l 2 Składuje rejestr A do RAM wskazanego przez adres 16 bitowy [***] RAM(aa16) <- A
    START 0xAA 0 Start programu HAVR - musi być na pozycji zerowej pamięci programu HAVR


    [*] - jeśli dany port nie istnieje na docelowym mikroprocesorze lub jest to port systemowy danego systemu cyfrowego instrukcja nie jest zaimplementowana
    [**] - w mikroprocesorach AVR RAMSEG0 to strona zerowa 0x00-0xFF, w innych architekturach musi to być 8-bitowy obszar zawierający rejestry I/O
    [***] - tylko w systemach z RAM powyżej 1KB (HF-6050, HF-6128, HF-6158)
    [^] - po operacji rejestr flagowy jest ładowany do RAM 0x0000


    Organizacja pliku HAVR8

    W tym miejscu przenikają się standardy THFS i HAVR8

    OFFSETDESCRIPTION
    0x00 START code 0xAA (= FILE ID). System operacyjny THOS-1 sprawdza w ten sposób poprawność pliku HAVR8 podczas jego wykonania [^]
    0x01 Punkt wejścia programu HAVR8, wykonanie zaczyna się od tego adresu
    0x01-0xED Program HAVR8, kod lub dane
    0xEC Extra Code Segment - opcjowalny segment dodatkowy zawierający kod [^^]
    0xED Extra Data Segment - opcjowalny segment dodatkowy zawierający dane [^^^]
    0xEE HAVR version - system operacyjny powinien odmówić wykonania programu w razie gdy jego wersja jest starsza od wersji interpretera
    0xEF Hardware Compatibility Code - używane dla odróżnienia różnych podwersji sprzętowych HAVR8 dla danego mikroprocesora lub systemu
    0xF0-0xFF Nazwa pliku. Jest ona opcjowalna, jeśli niema nazwy pliku to pod offsetem 0xF0 musi być zapisane 0x00 a offsety 0xF1-0xFF mogą być wykorzystane przez HAVR np. do przechowywania danych


    [^] - w czasie wykonania programu HAVR komórka 0x00 może być zainicjowana do przechowywania danych
    [^^] - segment kodu musi być przeładowany do RAMSEG1 i dostępny instrukcjami LD2, LD3
    [^^^] - segment danych musi być dostępny instrukcjami LD2, LD3


    Hardware Compatibility Code

    Niekompatybilności architektur w różnych systemach wymusiły zastosowanie Hardware Compatibility Code. Np. system HF-6030 rezerwuje PORTD dla urządzeń systemowych, tak więc instrukcje dostępu do tego portu przez użytkownika nie są zaimplementowane na tym systemie. System operacyjny THOS-1 odmawia wykonania programu HAVR w razie wykrycia niekompatybilności sprzętowej. HCC umieszczony jest w pliku HAVR8 pod zdefiniowanycm offsetem 0xEF.

    HCCDESCRIPTION
    0x00 Kompatybilność nie ustalona
    0x13 HF-60215 (HAVR6)
    0x28 HF-6128
    0x30 HF-6030
    0x40 HF-6050, HF-6128, HF-6158 (systemy z RAM powyżej 1KB)
    0x48 HF-6048
    0x50 HF-6050
    0x58 HF-6158
    0x69 HF-6169
    0x78 HF-6030 + HF-6048
    0x80 HF-6030 + HF-6050
    0x98 HF-6048 + HF-6050
    0xFF Wszystkie systemy



    Kompilacja programów HAVR

    Kompilacja jest możliwa zarówno na samych systemach cyfrowych jak i w zewnętrznych kompilatorach. Pierwszy sposób jest szybszy (nie trzeba downloadować kodu z PC) ale mniej wygodny bo wewnętrzny kompilator nie jest relokowalny. Trzeba odgórnie założyć adresy różnych funkcji, co jednak w zakresie 8 bit nie jest takie trudne po pewnej praktyce. Natomiast zewnętrzny kompilator ma więcej możliwości, w dodatku napisane zostały odpowiednie makroinstrukcje ułatwiajace kompilację. Więcej informacji w [1].

    Do kompilacji używam bardzo zgrabnego kompilatora FASMW wraz z napisanym plikiem makro który definiuje wszystkie instrukcje i stałe systemowe.

    HAVR_INS30.ASM plik makro definicji instrukcji dla systemu HF-6030
    HAVR_INS50.ASM plik makro definicji instrukcji dla systemu HF-6050


    Biblioteka programów HAVR

    Do chwili obecnej powstało około 150 programów HAVR, rozwiązujących różne problemy programistyczne, ale głównie są to sterowniki do różnych modułów zewnętrznych. Jednym z ciekawszych programów HAVR (choć mało użytecznym) jest gra w statki... Napisałem ją głównie dla zabawy i przetestowania możliwości HAVR. Co prawda jest to gra tylko z jednym graczem, komendy od programu odbiera się telegrafią, rysuje na kartce mapę a po zatopieniu wszystkich statków program podaje ilość strzałów do floty... Powstały 3 programy które różnią się tylko mapami statków.

    SHIPS01.ASM SHIPS01 - kod źródłowy
    SHIPS02.ASM SHIPS02 - kod źródłowy
    SHIPS03.ASM SHIPS03 - kod źródłowy


    CALCULATOR - jest to prosty 9-działaniowy, 8 bitowy kalkulator. Kod maszynowy zajął 191 bajtów pamięci. Dostępne działania to A - AND, C - NOT, D - ADD, N - NEG, O - OR, S - SUB, X - XOR, L - LSL, R - LSR.

    CALCULATOR.ASM CALCULATOR - kod źródłowy
    CALCULATOR.BIN CALCULATOR - kod maszynowy


    Dla modułu RTC HF-9037 powstały 3 programy, pierwszy to prosty test który testuje moduł licząc od 00:00, drugi ustawia datę i czas a trzeci to zegar który podaje czas co minutę.

    HF9037_TEST.ASM Test modułu HF-9037 - kod źródłowy
    HF9037_TEST.BIN Test modułu HF-9037 - kod maszynowy
    HF9037_SET.ASM Ustawianie daty i czasu modułu HF-9037 - kod źródłowy
    HF9037_SET.BIN Ustawianie daty i czasu modułu HF-9037 - kod maszynowy
    HF9037_CLOCK.ASM Zegar na bazie modułu HF-9037 - kod źródłowy
    HF9037_CLOCK.BIN Zegar na bazie modułu HF-9037 - kod maszynowy


    Lista systemów cyfrowych z zainstalowanym HAVR (w kolejności uruchamiania):

  • HF-6060 (AT90S2313) - pierwszy eksperymentalny system z interpreterem HAVR (działał ale miał niewielkie błędy)
  • HF-6048 (ATMEGA16) - pierwszy w pełni działający system z kompilatorem i interpreterem HAVR
  • HF-6030 (ATMEGA8) - najczęściej używany system z kompilatorem i interpreterem HAVR
  • HF-6050 (ATMEGA128)
  • HF-6128 (ATMEGA128)
  • HF-6158 (ATMEGA64)
  • HF-60215 (ATTINY13) - prowadzone próby z HAVR6
  • HF-6169 (ATMEGA64)
  • HF-60104 (8051) - w trakcie przepisywania na MCS-51


  • Planowane jest uruchomienie HAVR na wielu różnych (egzotycznych) mikroprocesorach, jest to jednak proces dość powolny który zajmie trochę czasu...


    Sources:

    [1] Oryginalna specyfikacja HAVR


    HF Register

    Microcontrollers in my constructions

    UC Database - my CPU collection

    Electronics

    Main Page