;Betriebssystem Space-Fight
;(c) by Thilo Ackermann 2004
;Start: 01.11.2004
;assemblieren mit nasm -f bin -o spiel.bin spiel.asm
;Image direkt auf Diskette schreiben, z.B. mit rawrite
;Ziel: Spiel, welches kein Betriebssystem benötigt auf einem 80286 Rechner
;mit maximal 2kb Speicher auf der Diskette

;Updates: 26.11.2016:
;   SourceCode in UTF-8
;   Kleineres Fixes
;   Auch von USB Stick bootbar
;   BIOS Boot Parameter Block weil einige Biose quatsch machen
;   HLT Instruktion eingeführt, damit überall Energie gespart wird

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;BOOT-VORGANG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

BITS 16
CPU 186

var_inttostrbuffer      equ 2048
var_kbdbufferpos        equ 2054
var_kbdbuffer           equ 2055
var_kbdtimerevents      equ 2955
var_myshipxpos          equ 2956
var_objectsbuffer       equ 2958
var_myshipmovement      equ 3470
var_myshipfire          equ 3471
var_enemycounter        equ 3472
var_rand                equ 3473
var_timercounter        equ 3475
var_score               equ 3476
var_health              equ 3478

boot_anfang:                            ;dieses label wird später benötigt
;Hier kommt leider so ein Quatsch, weil manche Biose den BIOS Parameter Block überschreiben
    jmp near .skipBiosParameterBlock
.systemName:
    db "TACKBOOT"
.endOfSystemName:
    times 8-(.endOfSystemName-.systemName) db 32
    dw 512          ;Bytes per Sector
    db 1            ;Sectors per Cluster
    db 1            ;Reserved Sector
    db 0            ;Fats on Disc
    dw 0            ;Root Directory entries
    dw 4
    db 0            ;Media Descriptor
    dw 7            ;Sectors per FAT
    dw 18           ;Sectors per Track
    dw 2            ;Sides
    times 10 db 0
    nop
    nop
.skipBiosParameterBlock:

    ;stack setzen
    cli                                     ;Interrupts aus
    xor sp,sp                               ;stackpointer auf 0
    mov ax,3000h                            ;segment 2000h soll stacksegment werden
    mov ss,ax                               ;nun aber setzen
    sti                                     ;Interrupts wieder an

    push dx                                 ;Boot Laufwerk sichern

    cld                                     ;direction flag löschen

    ;an eine "bessere" stelle (seg 1000h) kopieren
    ;quelldaten
    push cs
    pop ds
    ;mov ax,cs                               ;codesegment nach ax
    ;mov ds,ax                               ;ax nach datensegment --> ds=cs
    mov si,07c00h                           ;aktueller offset des bootloaders
    ;zieldaten
    push 1000h                              ;zielsegment
    pop es                                  ;und nun nach es
    xor di,di                               ;di auf 0
    ;kopieren
    mov cx,256                              ;256 words sollen kopiert werden (2*256=512)
    rep movsw                               ;kopieren
    ;in das ziel springen
    db 0eah
    dw boot_kopieren_ende
    dw 1000h
    boot_kopieren_ende:

    ;alles löschen
    mov di,2048
    mov cx,65536-2048
    xor al,al
    rep stosb

    ;nun gehts richtig los

    ;segmente setzen
    push cs
    pop es
    ;mov ax,cs                               ;codesegment nach ax
    ;mov es,ax                               ;es = cs

    ;restlichen 3 sektoren nachladen
boot_nachladen:
    ;es ist schon gesetzt
    mov bx,512                              ;an offset 512, da bytes 0-511 schon belegt sind
    mov ax,0203h                            ;funktionsnummer 2 und 3 sektoren
    mov cx,2                                ;beginne bei sektor 2 (erster sektor ist hier 1)
    pop dx                                  ;Boot Laufwerk wiederherstellen
    xor dh,dh                               ;kopf 0
    push dx
    int 13h
    jc boot_nachladen                       ;bei einem fehler, neu lesen
    
    cli
    
    ;timer auf 20 hz stellen
    mov al,00110110b                        ;chip einstellungen (timer0,schreiben low/high,
                                            ;rechteckwelle, 16-bit format
    out 43h,al                              ;und ausgeben
    mov al,0Bh                              ;lobyte für interval
    out 40h,al                              ;und speichern
    mov al,0e9h                             ;hibyte für interwall
    out 40h,al
    
    ;tastaturtreiber und timer setzen
    xor ax,ax                               ;segment 0
    mov ds,ax                               ;nach ds
    mov ax,cs
    mov bx,8*4
    push word [bx]                          ;timer interrupt alter offset sichern
    mov word [bx],fkt_timer                 ;in die interrupt vektortabelle eintragen
    push word [bx+2]                        ;timer interrupt altes segment sichern
    mov word [bx+2],ax

    mov word [bx+4],fkt_keypress            ;in die interrupt vektortabelle eintragen
    mov word [bx+6],ax                      ;codesement eintragen
    mov ds,ax                               ;ds = cs
    
    
;funktion expandfont entpackt die schriftart
fkt_expandfont:
    mov si,dat_schrift                          ;startadresse der schrift
    mov di,4000                                 ;zieladresse der entpackten schriftart
    mov cl,27*7                                 ;anzahl der bytes ermitteln
.schleife1:
    lodsb                                       ;nächstes Byte der gepackten Schrift einlesen
    mov dl,8                                    ;Bitzähler auf 8
.schleife2:
    stosb                                       ;Einspeichern
    shr al,1                                    ;al um eins nach rechts schieben
    dec dl                                      ;Bitzähler reduzieren
    jnz .schleife2                              ;Solange noch nicht 0, wiederholen
    loop .schleife1                             ;Nächstes komprimiertes Byte einlesen
    
    ;Hier wird die Timer Interrupt Routine gepatched, damit noch die BIOS Routine aufgerufen wird
    ;Hierzu muss die Schrift bereits entpackt sein, da 4 Bytes von der Schrift überschrieben werden
    pop word [fkt_timer_old_segment]                         ;segment speichern
    pop word [fkt_timer_old_offset]                         ;offset speichern
    
;farbtabelle generieren
fkt_setvga:
    ;grafikmodus setzen
    mov ax,13h                              ;modus 13h
    int 10h                                 ;setzen
    
    mov dx,03c8h                            ;portnummer für farbtabelle
    xor al,al                               ;beginne bei farbe 0
    out dx,al                               ;ausgeben
    inc dx                                  ;nächster port
    xor bl,bl                               ;bx auf 0
.loop1:
    xor ah,ah                               ;cx auf 0
.loop2:
    xor al,al                               ;ax auf 0
.loop3:
    push ax
    out dx,al
    mov al,ah
    out dx,al
    mov al,bl
    out dx,al
    pop ax
    
    add al,12
    cmp al,6*12                             ;6 farbstufen?
    jne .loop3                              ;wenn nicht dann nochmal
    add ah,12
    cmp ah,6*12                             ;6 farbstufen?
    jne .loop2                              ;wenn nicht dann nochmal
    add bl,12
    cmp bl,6*12                             ;6 farbstufen?
    jne .loop1                              ;wenn nicht dann nochmal
    
fkt_expand_all_pic:
    mov si,dat_compressed_images
    mov di,1024*6
    mov bp,5
.schleife1:
    push di
    ;Hier wird nun das einzelne Bild dekomprimiert (Quelle si)
    ;Am Ende wird si auf das nächste komprimierte Bild zeigen
    ;di Zeigt auf das Ziel
    mov cl,[si]                             ;pixelanzahl ermitteln
    xor ch,ch
    inc si
    movsb                                   ;Breite / Höhe kopieren
    mov dx,si                               ;dx enthält source+3, zeigt damit auf die mini farbtabelle
    add si,4                                ;farbtabelle überspringen

.schleife2:
    lodsb
    mov bl,al                               ;bl ist eine kopie von al
    shr al,3                                ;al um drei nach rechts -> linienlänge
    and bl,111b                             ;bl enthält die farbnummer
    xor bh,bh                               ;bh auf 0
    add bx,dx                               ;startadresse anfügen
    mov bl,[bx]
.schleife3:
    mov [di],bl
    inc di
    dec al
    jne .schleife3
    loop .schleife2
    pop di
    add di,1024
    dec bp
    jnz .schleife1

    
;hier wird das explosionsbild erzeugt
fkt_genexplode:
    mov di,1024*12
    mov cx,25*25
    mov al,26
    stosb
    stosb
.schleife:
    push cx
    mov cx,6
    call fkt_random
    pop cx
    mov bx,dx
    mov al,[bx+dat_farbexplo]
    stosb
    loop .schleife

    sti                                     ;interrupts wieder zulassen
    jmp fkt_ctrlgame
    
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;TASTATUR-FUNKTIONEN;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;funktion keypress wird bei drücken/loslassen einer taste aufgerufen
fkt_keypress:
    ;cli                                     ;interrupts ausschalten
    pusha                                   ;alle register sichern
    ;push ds
    ;mov ax,1000h
    ;mov ds,ax
    in al,60h                               ;taste einlesen

    mov si,var_kbdbufferpos
    mov bl,[si]                             ;pufferposition
    cmp bl,128                              ;puffer voll? (schon 128?)
    je .ende                    ;dann ende
    xor bh,bh                               ;bh auf 0
    ;add bx,2055                             
    mov [bx+var_kbdbuffer],al                        ;puffer beginnt bei 2055
    inc byte [si]                           ;pufferzähler erhöhen

.ende:
    mov al,20h                              ;20h nach al
    out 20h,al                              ;interrupt bestätigen
    ;pop ds
    popa                                    ;alle register wieder herstellen
    ;sti                                     ;wieder an
    iret

;funktion getkey ermittelt den letzten tastaturcode
;rückgabewert: al=tastaturcode, ah=1 bei leerem puffer, sonst 0
fkt_getkey:
    cli                                     ;interrupts aus
    mov si,var_kbdbufferpos
    cmp byte [si],0                         ;gar kein zeichen im puffer?
    je .leer
    dec byte [si]                           ;zähler um eins veringern
    mov al,[var_kbdbuffer]                             ;zeichen ermitteln
    ;aufrücken
    ;mov cl,127
    ;xor ch,ch
    mov cx,127
    inc si

.schleife1:
    mov dl,[si+1]
    mov [si],dl
    inc si
    ;dec cl
    ;cmp cl,0
    ;jne fkt_getkey_schleife1
    loop .schleife1

    xor ah,ah                               ;mind. 1 zeichen war im puffer
    sti                                     ;interrupts wieder an
    ret

.leer:
    mov ah,1                                ;puffer leer
    sti                                     ;interrupts an
    ret
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;AUSGABE-FUNKTIONEN;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;funktion displayint wandelt eine zahl in einen nullterminierten string um
;und gibt diesen aus
;ersetzt (fkt_inttostr in kombination mit fkt_print)
;parameter: di:pos, ch farbe, ax zahl

fkt_displayint:
    push cx
    mov si,var_inttostrbuffer+6
    mov bx,10                               ;teiler bx
.schleife1:
    xor dx,dx                               ;dx auf 0
    div bx                                  ;ax/bx (zahl/10)
    add dl,'Z'+1                        ;zum rest 30h addieren ('0')
    dec si                              ;eins weiter nach vorne im puffer
    mov [si],dl                         ;zeichen in den puffer speichern
    cmp ax,0                            ;alle ziffern ausgegeben?
    jne .schleife1                      ;wenn nicht, dann nochmal
    pop dx
    ;Hier fällt es einfach in die nächste Funktion
    ;fkt_print

;funktion print
;parameter: ds:si adresse des strings (nullterminiert)
;parameter: di:pos, dh farbe
fkt_print:
.schleife_aussen:
    lodsb                                   ;ein byte lesen
    cmp al,0                                ;0?
    je .ende                       ;wenn wirklich null, dann ende
    ;mov ah,0eh                              ;funktionsnummer zeichen ausgeben
    ;mov bx,7                                ;farbe 7
    ;int 10h                                 ;bios funktion 10h

    cmp al,32                               ;leerzeichen überspringen
    je .weiter1
    sub al,'A'
    push di
    
    ;mov cl,6                                ;zeichen mal 6 nehmen
    ;mul cl                                  ;in ax sind nun 6 zeichen
    ;mov bx,ax                               ;nach bx
    xor ah,ah
    shl ax,1
    mov bx,ax
    shl ax,1
    add bx,ax

    mov cx,7                                ;7 zeilen zähler auf 7
.schleife1:
    ;xor dl,dl                               ;6 pixel zähler auf 0
    mov dl,6
.schleife2:
    test byte [bx+4000],1                       ;pixel leer?
    je .weiter                  ;wenn nicht, dann überspringen
    mov [fs:di],dh                          ;pixel setzen
.weiter:
    inc bx
    inc di
    dec dl
    jnz .schleife2
    add di,320-6
    add bx,216-6                                ;nächste zeile des zeichens
    loop .schleife1
    
    ;mov ch,dh
    pop di
.weiter1:
    add di,8
    jmp .schleife_aussen
.ende:
    ret

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;GRAFIK-FUNKTIONEN;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;funktion drawbuffer zeichnet den hintergrundpuffer auf den bildschirm
;zuerst auf vertical retrace warten
fkt_drawbuffer:
    push ds
    mov dx,03dah                            ;portadresse für vga-status
.schleife1:
    in al,dx                                ;einlesen
    test al,1000b                           ;bit 3 gesetzt?
    jne .schleife1            ;wenn nicht, dann nochmal zeichnen
;und nochmal prüfen
.schleife2:
    in al,dx                                ;einlesen
    test al,1000b                           ;bit 3 gesetzt?
    jne .schleife2
    ;mov ax,2000h                            ;segment des grafikpuffers
    ;mov ds,ax                               ;nach quellsegment
    push 0x2000
    pop ds
    xor si,si                               ;si auf 0
    push 0xA000
    pop es
    ;mov ax,0A000h                           ;segment der grafikkarte
    ;mov es,ax                               ;nach zielsegment
    xor di,di                               ;di auf 0
    mov cx,320*200/2                        ;den gesamten bildschirm in words kopieren
    rep movsw
    pop ds
    ret

;funktion clearscreen löscht den gesamten bildschirm
fkt_clearscreen:
    push 0x2000
    pop es
    xor di,di
    mov cx,320*200/2
    xor ax,ax
    rep stosw
    ret



times 14 nop
boot_ende:                              ;nun kommt das ende des ersten sektors
    times 510-(boot_ende-boot_anfang) nop   ;ungenutzer speicher mit nop auffüllejn
    dw 0aa55h                               ;bootsektor signatur anhängen
    
;funktion showpic zeigt ein bild an

;parameter: si: quelldaten des bildes, ax: ypos, di:xpos
fkt_showpic:
    ;xor ch,ch
    ;mov cl,25                               ;höhe
    mov cx,25

    cmp ax,0                                ;ypos mit 0 vergleichen
    jl .kleiner                  ;dann zur kleiner stelle springen
    mov dx,ax
    add dx,cx
    cmp dx,199                              ;ypos >199
    jg .grosser                  ;dann zur größer stelle springen
    mov bx,320                              ;zeilengröße
    mul bx                                  ;sonst mit 320 multiplizieren
    add di,ax                               ;und zu di addieren
    xor ax,ax                               ;keine si-verschiebung
    jmp .weiter

.kleiner:
    neg ax                                  ;vorzeichen umkehren
    sub cx,ax                               ;von der bildhöhe subtrahieren
    mov bl,[si]                             ;*breite
    xor bh,bh
    mul bx                                  ;ax*bx ax=si-verschiebung
    jmp .weiter

.grosser:
    push dx
    mov bx,320                              ;zeilengröße
    mul bx                                  ;sonst mit 320 multiplizieren
    add di,ax                               ;und zu di addieren
    pop dx
    sub dx,200                              ;200 abziehen
    sub cx,dx                               ;abziehen
    xor ax,ax                               ;keine si-verschiebung

.weiter:
    mov dl,cl
    mov dh,[si]                             ;breite
    xor ch,ch
    ;add si,2
    inc si
    add si,ax                               ;verschiebung addieren

    mov ax,fs                                 ;fs nach...
    mov es,ax                                  ;... es

    mov al,dh                               ;in ax ist jetzt die breite
    xor ah,ah
.schleife1:
    mov cl,al                               ;breite nach cl (ch ist 0)
    rep movsb                               ;kopieren
    add di,320                              ;nächste zeile
    sub di,ax                               ;breite wieder abziehen
    dec dl                                  ;höhe veringern
    cmp dl,0                                ;alle zeilen ausgegeben?
    jne .schleife1               ;wenn nicht, dann nochmal
    ret

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;SPIEL-FUNKTIONEN;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;funktion ctrlgame kontrolliert das spiel
fkt_ctrlgame:
    mov ax,2000h
    mov fs,ax
    push cs
    pop es
    ;menü
    ;für neues spiel vorbereiten
    ;TODO clear positionen überarbeiten
    ;TODO check ob es gesetzt
    mov di,2955
    mov cx,3999-2955
    xor al,al
    rep stosb
    mov byte [var_health],100                     ;rumpf auf 100
    ;menü anzeigen
    call fkt_clearscreen                    ;löschen des puffers
    mov si,dat_willkommen
    mov di,10*320+320/2-25*8/2
    mov dh,150
    call fkt_print
    mov si,dat_leer
    mov di,100*320+320/2-18*8/2
    mov dh,5
    call fkt_print
    mov si,dat_esc
    mov di,120*320+320/2-13*8/2
    mov dh,5
    call fkt_print
    call fkt_drawbuffer                     ;puffer auf bildschirm bringen

fkt_ctrlgame_menu:
    mov [var_kbdtimerevents],byte 0                       ;timer auf 0
    hlt
    call fkt_getkey                         ;taste ermitteln
    cmp ah,1                                ;keine taste gedrückt?
    je fkt_ctrlgame_menu                    ;dann weiter
    cmp al,57                               ;leertaste?
    je fkt_ctrlgame_main
    cmp al,1
    jne fkt_ctrlgame_menu
    ;ret                                     ;im menü ende auswählen
    db 0Eah
    dw 0000h
    dw 0FFFFh

;spiel
fkt_ctrlgame_main:
    hlt
    call fkt_getkey                         ;taste ermitteln
    cmp ah,1                                ;keine taste gedrückt?
    je .kbdDone                 ;dann weiter

    cmp al,0fah                             ;bestätigungscode?
    je .kbdDone                 ;dann weiter

    cmp al,224                              ;erweiterter code
    jne .kbdNonExtended                ;nein, dann weiter

.kbdWaitExtended:
    ;TODO hier ein HLT irgendow
    call fkt_getkey                         ;noch eine taste
    cmp ah,1                                ;gedrückt?
    je .kbdWaitExtended               ;wenn nicht, dann nochmal

    mov di,var_myshipmovement
    cmp al,75
    je .kbdLeft
    cmp al,77
    je .kbdRight
    mov byte [di],0
    jmp .kbdDone                ;weiter

.kbdRight:
    mov byte [di],1
    jmp .kbdDone
.kbdLeft:
    mov byte [di],2
    jmp .kbdDone


.kbdNonExtended:
    cmp al,57
    je .kbdFire
    cmp al,1
    jne .kbdDone
    jmp fkt_ctrlgame_gameover

.kbdFire:
    mov byte [var_myshipfire],1

.kbdDone:


    mov di,var_kbdtimerevents
    cmp byte[di],0                          ;timer kontrolle
    je fkt_ctrlgame_main                    ;wenn kein timersignal dann nochmal
    dec byte[di]                            ;timersignal entfernen

    mov di,var_timercounter
    inc byte [di]                           ;timerzähler erhöhen
    cmp byte [di],7                         ;mit 32 vergleichen
    jne fkt_ctrlgame_weiter9
    mov byte [di],0
fkt_ctrlgame_weiter9:

    mov di,var_myshipmovement
    mov si,var_myshipxpos
    cmp [di],byte 1                         ;nach rechts?
    je .meMoveRight
    cmp [di],byte 2                         ;nach links?
    je .meMoveLeft
    jmp .meMoveDone

.meMoveRight:
    cmp word [si],320-25
    je .meMoveDone
    add word [si],5
    jmp .meMoveDone

.meMoveLeft:
    cmp word [si],0
    je .meMoveDone
    sub word [si],5
    
.meMoveDone:
    mov di,var_myshipfire
    cmp byte [di],1                       ;rakete ablassen
    jne fkt_ctrlgame_weiter5
    cmp byte [var_timercounter],0                       ;vergleichen ob timerzähler auf 0
    jne fkt_ctrlgame_weiter5
    mov ax,[var_myshipxpos]

    add ax,25/2-5/2
    ;mov dl,200-25
    ;mov dh,5
    mov dx,05afh
    ;mov bl,8
    ;mov bh,0
    mov bx,8
    mov byte [di],0
    call fkt_objekt

fkt_ctrlgame_weiter5:
    call fkt_clearscreen
    ;zeichnen deines raumschiffes
    mov di,[var_myshipxpos]
    mov ax,200-25
    mov si,1024*6
    call fkt_showpic

    ;objekte
    ;;;;;;;;
    xor cx,cx                               ;zähler auf 0
    mov di,var_objectsbuffer                             ;startadresse der objekte
    mov byte [var_enemycounter],0                       ;raumschiffanzahl auf 0
fkt_ctrlgame_schleife2:
    push di                                 ;di sichern
    push cx                                 ;cx sichern
    cmp byte [di+3],0                       ;ist dieses objekt benutzt?
    je fkt_ctrlgame_weiter3                 ;wenn nicht, dann an das ende der schleife

    cmp byte [di+5],0                       ;nach oben?
    je fkt_ctrlgame_hoch
    call fkt_movedown
    jmp fkt_ctrlgame_weiter6
fkt_ctrlgame_hoch:
    call fkt_moveup

fkt_ctrlgame_weiter6:

    mov bl,[di+3]                           ;typ laden
    cmp bl,0                                ;ist dieses objekt benutzt?
    je fkt_ctrlgame_weiter3                 ;wenn nicht, dann an das ende der schleife

    ;bild anzeigen (ax ist schon gesetzt)
    cmp bl,7                                ;ist es feuer?
    jae fkt_ctrlgame_weiter10
    cmp bl,4                                ;ist es ein schuss
    ja fkt_ctrlgame_weiter7                 ;dann direkt weiter
    inc byte [var_enemycounter]                         ;sonst raumschiffanzahl erhöhen
    call fkt_checkcrash
    jmp fkt_ctrlgame_weiter10

;dein schuss
fkt_ctrlgame_weiter7:
    push ax
    push bx
    call fkt_checkrakete
    pop bx
    pop ax

fkt_ctrlgame_weiter10:
    sub bl,2
    mov bh,0
    shl bx,10
    mov si,bx
    add si,7*1024
    mov di,[di]                             ;xpos laden
    call fkt_showpic                        ;und bild öffnen

fkt_ctrlgame_weiter3:
    pop cx                                  ;cx wiederherstellen
    pop di                                  ;di wiederherstellen
    add di,8                                ;nächstes objekt 8 bytes weiter
    inc cx                                  ;nächstes objekt
    cmp cx,64                               ;64 objekte?
    jne fkt_ctrlgame_schleife2              ;wenn nicht, dann nochmal

    cmp [var_enemycounter],byte 5                       ;genug raumschiffe?
    ja fkt_ctrlgame_weiter8

    ;random x-position
    mov cx,290
    call fkt_random
    push dx

    ;random geschwindigkeit
    mov cx,10
    call fkt_random
    inc dl
    mov bl,dl                               ;geschwindigkeit
    mov bh,1                                ;nach unten
    push bx

    ;random raumschifftypen
    mov cx,3
    call fkt_random
    add dl,2
    mov dh,dl

    pop bx
    pop ax

    mov dl,1                                ;y-pos
    call fkt_objekt

fkt_ctrlgame_weiter8:

    ;rumpftext
    mov si,dat_rumpf
    xor di,di
    mov dh,215
    call fkt_print

    mov al,[var_health]
    cmp al,0
    je fkt_ctrlgame_gameover
    xor ah,ah
    mov di,7*8
    mov ch,215
    call fkt_displayint

    ;punktetext
    mov si,dat_punkte
    mov di,8*320
    mov dh,215
    call fkt_print

    mov ax,[var_score]
    mov di,8*8+8*320
    mov ch,215
    call fkt_displayint


    ;zeichnen
    call fkt_drawbuffer
    jmp fkt_ctrlgame_main

fkt_ctrlgame_gameover:
    call fkt_clearscreen                    ;löschen des puffers
    mov si,dat_gameover
    mov di,30*320+320/2-9*8/2
    mov dh,150
    call fkt_print
    mov si,dat_leer
    mov di,100*320+320/2-18*8/2
    mov dh,5
    call fkt_print
    call fkt_drawbuffer                     ;puffer auf bildschirm bringen

fkt_ctrlgame_ctrlgameover:
    hlt
    call fkt_getkey                         ;taste ermitteln
    cmp ah,1                                ;keine taste gedrückt?
    je fkt_ctrlgame_ctrlgameover            ;dann weiter
    cmp al,57                               ;leertaste?
    je fkt_ctrlgame
    jmp fkt_ctrlgame_ctrlgameover

;funktion objekt erstellt eine neues objekt
;parameter: ax=xpos, dl=ypos, dh=typ, bl=geschwindigkeit, bh=richtung
fkt_objekt:
    xor cx,cx
    mov di,var_objectsbuffer
.schleife1:
    cmp byte [di+3],0
    je .gef
    add di,8
    inc cx
    cmp cx,64
    jne .schleife1
    ret
.gef:
    mov [di],ax
    mov [di+2],dx
    mov [di+4],bx
    ret

;funktion moveup bewegt das objekt in di nach oben und entfernt es, wenn es außerhalb des randes ist
fkt_moveup:
    mov al,[di+2]                           ;y-position
    mov cl,[di+4]                           ;geschwindigkeit
    cmp al,cl                               ;mit geschwindigkeit vergleichen
    jb fkt_delobj                           ;dann entfernen
    sub [di+2],cl                           ;y-position erniedriegen
    xor ah,ah                               ;ah auf 0
    sub ax,25                               ;25 pixel in das minus
    ret

;funktion delobj löscht ein objekt (funktion mit jmp aufrufen)
fkt_delobj:
    mov byte [di+3],0                       ;typ auf 0
    ret

;funktion movedown bewegt das objekt in di nach unten und entfernt es, wenn es außerhalb des randes ist
fkt_movedown:
    mov al,[di+2]                           ;y-position
    mov cl,[di+4]                           ;geschwindigkeit
    cmp al,220                              ;mit 200 vergleichen
    ja fkt_delobj                           ;dann entfernen
    add [di+2],cl                           ;y-position erniedriegen
    xor ah,ah                               ;ah auf 0
    sub ax,25                               ;25 pixel in das minus
    ret

;funktion random berechnet einen zufallswert
;parameter: cx: 0-(cx-1)
;rückgabe: dx rest
fkt_random:
    mov ax,[var_rand]                           ;faktor1
    mov dx,8405h                            ;faktor2
    mul dx
    inc ax
    mov [var_rand],ax
    mov ax,dx                               ;rest nach ax
    xor dx,dx                               ;dx auf 0
    div cx                                  ;durch cx
    ret

;funktion checkrakete überprüft, ob ein raumschiff von der rakete getroffen wurde
fkt_checkrakete:
    ;xor cx,cx
    mov cx,64
    mov si,var_objectsbuffer
    mov dl,[di+2]                           ;y-position der rakete
    mov bx,[di]                             ;x-position der rakete
.schleife:
    cmp [si+3], byte 0                      ;ist das überhaupt ein objekt?
    je .weiter1
    cmp [si+3], byte 4                      ;ist das eine rakete?
    ja .weiter1              
    ;hier ein raumschiff
    mov al,[si+2]                           ;y-pos des raumschiffes
    add al,25
    cmp dl,al                               ;rakete größer raumschiff?
    ja .weiter1              ;dann nicht kaputtgehen
    mov ax,[si]                             ;x-pos des raumschiffes
    cmp bx,ax                               ;xpos der rakete kleiner?
    jb .weiter1
    add ax,25                               ;breite des raumschiffes + xpos
    cmp bx,ax                               ;xpos der rakete größer als ax?
    ja .weiter1
    xor ah,ah
    mov al,[si+3]                           ;typ des raumschiffes
    shl ax,1                                ;x2
    add [var_score],ax                           ;punkte addieren
    mov [si+3],byte 7                       ;explosion
    mov [si+4],byte 10                      ;geschwindigkeit hoch
    mov [di+3],byte 0                       ;rakete löschen
    ret
.weiter1:
    add si,8
    ;inc cx
    ;cmp cx,64
    ;jne fkt_checkrakete_schleife
    loop .schleife
    ret

;funktion checkcrash überprüft ob ein raumschiff mit deinem raumschiff zusammengeprallt ist
fkt_checkcrash:
    ;y-check
    cmp [di+2],byte 200-25                           ;y koordinate des feinds prüfen, noch nicht unten?
    jb .weiter
    ;x-check
    mov cx,[di]                             ;x-koordinate des raumschiffes
    add cx,25                                   ;rechter rand des anderen raumschiffes
    mov dx,[var_myshipxpos]                           ;deine koordinate
    cmp cx,dx                                   ;vgl rechter rand des feinds mit deinem linken rand
    jb .weiter
    ;add dx,25
    ;sub cx,25
    add dx,50                                   ;rechter rand eigentlich +25, da aber cx um 25 zu groß ist +50
    cmp cx,dx                                   ;vgl linker rand des feinds mit deinem rechten rand
    ja .weiter

    xor ch,ch
    mov cl,[di+3]                           ;typ des raumschiffes
    shl cx,1                                ;x2
    add [var_score],cx                           ;punkte addieren
    mov [di+3],byte 7                       ;explosion
    sub byte [var_health],5
.weiter:
    ret


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;TIMER-FUNKTIONEN;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

fkt_timer:
    ;pusha
    push ds
    push 0x1000
    pop ds
    ;mov ax,1000h                            ;aktuelles segment
    ;mov ds,ax                               ;nach ds
    ;timertick

    inc byte [var_kbdtimerevents]
    pop ds
    
    db 0eah
fkt_timer_old_offset:
    ;dw 0
fkt_timer_old_segment equ fkt_timer_old_offset+2
    ;dw 0
    ;ACHTUNG die folgenden 4 Byte werden überschrieben, daher ist die schrift
    ;zu entpacken, bevor der timer initialisiert wird
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;DATEN-BEREICH;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;dat_schriftlen db 27*7
;dat_schriftwidth db 216
dat_schrift:
db 204, 231, 127, 255, 239, 133, 204, 31, 5, 225, 232, 61, 222, 227, 125, 97, 24, 134, 225, 239, 33, 222, 23, 252, 222, 239, 121
db 82, 24, 132, 65, 16, 132, 12, 152, 4, 243, 25, 70, 97, 20, 16, 97, 24, 206, 33, 24, 50, 33, 152, 4, 33, 24, 134
db 82, 24, 132, 65, 16, 132, 12, 88, 4, 109, 27, 70, 97, 20, 16, 161, 20, 122, 18, 20, 42, 16, 152, 4, 1, 20, 134
db 210, 23, 132, 223, 23, 252, 12, 184, 4, 97, 26, 62, 225, 227, 17, 161, 20, 50, 30, 211, 34, 136, 247, 127, 31, 239, 249
db 127, 24, 132, 65, 208, 133, 12, 152, 5, 97, 30, 6, 105, 1, 18, 161, 212, 122, 136, 16, 34, 4, 136, 128, 33, 18, 130
db 97, 24, 132, 65, 16, 134, 76, 24, 7, 97, 28, 6, 81, 2, 18, 33, 211, 206, 68, 16, 34, 2, 136, 128, 33, 18, 134
db 225, 231, 127, 127, 224, 135, 140, 23, 254, 97, 232, 5, 110, 228, 17, 30, 35, 133, 194, 239, 33, 255, 135, 124, 30, 226, 121

dat_compressed_images:
;dat_bild1:
db 65
db 25
db 0,197,134,0
db 248, 248, 248, 248, 88, 41, 152, 57, 144, 57, 144, 57, 144, 57, 144, 57, 96, 26, 24, 57, 24, 26, 48, 26, 24, 57, 24, 26, 48, 26, 24, 57, 24, 26, 48, 26, 24, 57, 24, 26, 48, 26, 24, 57, 24, 26, 48, 26, 24, 57, 24, 26, 48, 26, 24, 57, 24, 26, 24, 249, 249, 249, 249, 249, 161

;dat_bild2:
db 40
db 25
db 180,5,0,30
db 248, 248, 248, 248, 248, 248, 248, 248, 216, 249, 153, 10, 185, 26, 169, 42, 153, 58, 137, 74, 121, 90, 105, 106, 33, 27, 33, 122, 25, 27, 25, 138, 57, 154, 41, 162, 41, 170, 25, 90

;dat_bild3:
db 55
db 25
db 126,207,0,212
db 248, 248, 248, 248, 248, 248, 248, 248, 248, 40, 57, 128, 89, 104, 105, 48, 10, 40, 105, 40, 26, 40, 89, 40, 34, 56, 57, 56, 42, 144, 74, 120, 90, 16, 11, 56, 11, 16, 114, 11, 56, 11, 130, 11, 58, 11, 130, 11, 58, 11, 130, 11, 58, 11, 250

;dat_bild4:
db 61
db 25
db 181,0,147,112
db 200, 9, 184, 25, 168, 41, 152, 57, 136, 73, 24, 10, 56, 10, 24, 89, 16, 10, 56, 10, 16, 105, 8, 10, 56, 10, 8, 121, 10, 56, 10, 129, 10, 56, 10, 129, 10, 9, 40, 9, 10, 129, 10, 17, 24, 17, 10, 161, 11, 193, 11, 193, 11, 193, 11, 193, 11, 193, 11, 193, 11


;dat_bild5:
db 8
db 5
db 0,5,17,0
db 16, 9, 24, 25, 8, 41, 250, 234

;dat_bild6:
;db 21
;db 5,25
;db 0,50,215,30,0,0,0,0
;db 8, 25, 16, 25, 16, 25, 16, 25, 16, 25, 8, 241, 8, 25, 16, 26, 16, 26, 24, 10, 16

dat_farbexplo:
db 0,0,5,17,35,0

dat_rumpf: db "RUMPF",0
dat_punkte: db "PUNKTE",0
dat_esc: db "ESC FUER ENDE",0
dat_willkommen: db "WILLKOMMEN ZU SPACE FIGHT",0
dat_gameover: db "GAME OVER",0
dat_leer: db "LEERTASTE DRUECKEN",0
    
    
;die verbleibenden fünf byte kann ich jetzt ruhig verschwenden ;-)

times 171 nop

db "T.ACK"                              ;und eine schöne signatur ;-)

ende:
times 2048-(ende-boot_anfang) db 0      ;und weil es so schön ist, noch auf 2048 bytes auffüllen
