Partner WFG, 2FG, 1FG – PRIROČNIK ZA PROGRAMIRANJE

5. MOJ PRVI PROGRAMSKI PRODUKT

==============================

Ko program preraste eno samo datoteko, potrebuje red. Datoteke se razmnožijo, prevajanje postane zapleteno, končni rezultat pa ni jasno ločen od izvorne kode. Tu nastopi pravi programski produkt – projekt z jasno strukturo, Makefile sistemom in vlogo za vsak imenik.

V tem poglavju spoznamo, kako so organizirani resni programi za sistem Partner. Za vzor nam bo demonstracijski program iz repozitorija idp-app, ki je prosto dostopen na GitHubu:

https://github.com/iskra-delta/idp-app

5.1. OPIS PROGRAMA

Program sam po sebi ni zapleten – po zaslonu odskakuje štirinajst majhnih žog. Toda za tistega, ki ga je treba spraviti v tek, je to velik korak: grafika v ločljivosti 1024×256 točk, dvojno preklapljanje strani, zbirniška pisava za naslov in popoln Makefile, ki za vas prevede vse skupaj znotraj Docker vsebnika.

Na vrhu zaslona je naslov. Pod njim je omejeno polje, po katerem se premikajo žoge – vsaka s svojo hitrostjo in smerjo. Ko žoga zadene rob, se odbije. Animacija teče gladko, ker program nikoli ne riše neposredno na prikazano stran zaslona.

Namesto tega uporablja dvojno preklapljanje strani (double buffering): medtem ko se ena stran prikazuje, program tiho riše na drugo. Ko je nova slika pripravljena, strani zamenja z enim samim klicem. Gledalec nikoli ne vidi vmesnega stanja – le gladko gibanje. To je klasičen trik, ki so ga programerji izpilili v zgodnjih osemdesetih, ko so igre morale teči hitro na skromni strojni opremi.

Partner WFG, 2FG, 1FG – PRIROČNIK ZA PROGRAMIRANJE

5.2. PRENOS PROGRAMA

Program prenesete na PC računalnik z Linuxom z orodjem Git. Ukaz git clone ustvari nov imenik z vso izvorno kodo, datotekami Makefile in dokumentacijo:

git clone https://github.com/iskra-delta/idp-app

Po prenosu vstopite v imenik projekta:

cd idp-app

Zdaj ste pripravljeni na gradnjo.

5.3. STRUKTURA PROJEKTA

Po prenosu boste v imeniku videli naslednjo strukturo:

idp-app/
├── Makefile
├── README.md
└── src/
    ├── Makefile
    ├── idp8x16_font.s
    └── main.c

Vsak del ima svojo vlogo:

  • Makefile v korenu projekta je vstopna točka za gradnjo. Skrbi za zagon Docker vsebnika in prenese gradnjo nanj.
  • src/Makefile vsebuje dejansko logiko prevajanja: pravila za datoteke C in zbirnik, povezovanje ter ustvarjanje diskovne slike.
  • src/main.c je glavna izvorna datoteka z vso logiko animacije in grafike.
  • src/idp8x16_font.s je zbirniška datoteka s pisavo, s katero program izrisuje naslov na zaslonu.

Po uspešni gradnji sistem samodejno ustvari še dva imenika:

  • build/ – vmesna objektna koda in rezultati prevajanja,
  • bin/ – končni rezultati: demo.com in demo.img.
Partner WFG, 2FG, 1FG – PRIROČNIK ZA PROGRAMIRANJE

5.4. MAKEFILE

Projekt ima dve datoteki Makefile, vsako na svoji ravni. Skupaj tvorita elegantno ločitev med tem, kako se program prevaja, in tem, kje se prevaja.

Korenski Makefile je vstopna točka, ki jo kličete iz PC računalnika. Njegova naloga je preprosta: zagnati Docker vsebnik in v njem poklicati src/Makefile. Sam ne ve ničesar o prevajalniku – le pripravi okolje:

DOCKER_IMAGE ?= wischner/sdcc-z80-idp:latest

WORKDIR := $(CURDIR)
PROJECTS_DIR := $(abspath ..)
PROJECT_NAME := $(notdir $(WORKDIR))
UID := $(shell id -u)
GID := $(shell id -g)

DOCKER_RUN = docker run --rm \
    --user $(UID):$(GID) \
    -v "$(PROJECTS_DIR):/workspace" \
    -w "/workspace/$(PROJECT_NAME)" \
    $(DOCKER_IMAGE)

all:
    @$(DOCKER_RUN) make -C src all

Posebej zanimiva je vrstica z -v: v vsebnik se priklopi nadrejeni imenik projekta, ne projekt sam. S tem Docker vsebnik dobi dostop do imenikov build/ in bin/, ki se nahajata v korenu projekta in kamor se shranjujejo rezultati. Spremenljivki UID in GID zagotovita, da ustvarjene datoteke ostanejo vaše in ne v lasti korenskega uporabnika.

Korenski Makefile pozna tri cilje:

  • all – prevede projekt,
  • clean – izbriše imenika build/ in bin/,
  • docker-pull – prenese Docker sliko, preden začnete z delom.
Partner WFG, 2FG, 1FG – PRIROČNIK ZA PROGRAMIRANJE

src/Makefile vsebuje dejansko logiko prevajanja. Deluje znotraj Docker vsebnika in pozna vse podrobnosti orodne verige:

  • Vsako datoteko .c prevede z sdcc v objektno datoteko .rel in jo shrani v build/.
  • Vsako datoteko .s prevede z sdasz80 – to je zbirnik za Z80.
  • Ko so vse objektne datoteke pripravljene, jih poveže skupaj z grafično knjižnico ugpx.lib v datoteko .ihx.
  • sdobjcopy pretvori .ihx v izvršljivo datoteko demo.com.
  • cpmdisk ustvari diskovno sliko demo.img in nanjo doda demo.com.

Celotna veriga od izvorne kode do diskovne slike se sproži samodejno z enim samim ukazom.

5.5. PREVAJANJE

Gradnjo zaženete z ukazom:

make

Ob prvem zagonu Docker samodejno prenese sliko wischner/sdcc-z80-idp, če je še nimate lokalno. Nadaljnji zagoni so hitrejši, ker slika že obstaja. Po kratkem času boste v imeniku bin/ dobili:

bin/demo.com   ← izvršljiva datoteka CP/M
bin/demo.img   ← diskovna slika za emulator

Za brisanje vseh rezultatov gradnje:

make clean
Partner WFG, 2FG, 1FG – PRIROČNIK ZA PROGRAMIRANJE

5.6. ZAGON V EMULATORJU

Zaženite emulator PartEm in priključite bin/demo.img kot disk B:. Ko se prikaže ukazni poziv sistema Partner, zaženite program:

B:DEMO

Na zaslonu se prikaže naslov in po njem štirinajst žog, ki se odbijajo po polju. Program se po 200 sličicah zaključi sam od sebe in se vrne v ukazni poziv.

5.7. STANDARDNA STRUKTURA C PROJEKTA

Demonstracijski program idp-app je namerno preprost – ima le imenik src/ in rezultate v bin/. Ko projekt raste, je koristno vedeti, kakšna je ustaljena struktura večjih C projektov. Spoznajmo jo:

projekt/
├── src/         izvorna koda programa
├── include/     javne glave (.h), ki jih vidijo drugi moduli
├── lib/         knjižnice, ki jih projekt vključuje ali gradi
├── build/       objektna koda in vmesni rezultati prevajanja
├── bin/         končni izvršljivi programi in diskovne slike
├── tests/       avtomatski testi
└── docs/        dokumentacija

Vsak imenik ima jasno dogovorjeno vlogo:

  • src/ vsebuje vso izvorno kodo. Zasebne glave, ki jih potrebuje le ta modul, ostanejo tukaj, ne v include/.
  • include/ hrani javne glave – tiste, ki jih sme uvažati katerikoli drug del projekta ali zunanja knjižnica.
  • lib/ je namenjen knjižnicam: tistim, ki jih projekt sam gradi, ali tistim, ki jih vključuje od zunaj.
  • build/ nastane med prevajanjem in vsebuje objektne datoteke ter druge vmesne rezultate. Ta imenik nikoli ne sodí v repozitorij.
  • bin/ je cilj celotne gradnje. Sem pristanejo izvršljive datoteke in diskovne slike. Tudi ta imenik se praviloma ne shranjuje v repozitorij.
  • tests/ vsebuje avtomatske teste. Z njimi preverimo, da sprememba ni pokvarila obstoječe funkcionalnosti.
  • docs/ hrani dokumentacijo – opise vmesnikov, navodila za gradnjo in razlage zasnove.

Dobra struktura projekta ni le estetska odločitev. Je tista, ki vam bo po šestih mesecih dala vedeti, kam sodi nova datoteka.