Invasion (πρώην Defender) σε NTC BASIC

Το Invasion έγινε backport από την έκδοση του Defender που είχα φτιάξει για το ZX Spectrum Next, προτού αλλάξει ο τίτλος σε Invasion. Η αρχική κυκλοφορία του Defender ήταν εντάξει, σκέφτηκα «χμ, ένας CPC που τρέχει compiled BASIC – αυτό περίπου θα περίμενα», αλλά… δοκιμάζοντας ξανά το Invasion στο ZX Spectrum Next αφού είχα προγραμματίσει το Cyborg Hellgate, παρατήρησα μια περίεργη συμπεριφορά – όσο περισσότερους εχθρούς σκότωνα, τόσο πιο γρήγορο γινόταν το παιχνίδι – και… ο CPC κληρονόμησε το ίδιο ακριβώς bug. Τώρα η έκδοση για CPC (όπως και εκείνη για το ZX Spectrum Next) πετάει, πάει πάααρα πολύ καλά. Είναι ο καλύτερος κλώνος του Defender στον CPC; Δεν ξέρω – έχω ακόμα μια αδυναμία στο Defend or Die, κι αν δεν μου κράσαρε τυχαία τόσο συχνά, θα διάλεγα το Defend or Die για λόγους αυθεντικότητας.

Αφαίρεσα την inline assembly για να είναι πιο ευανάγνωστος ο κώδικας, αλλά το πρόγραμμα αρχικά γράφτηκε 100% σε NTC BASIC και μετά βελτιστοποιήθηκε – πράγμα που αφορούσε κυρίως την κίνηση των αντικειμένων, καθώς ο CPC δεν διαθέτει sprites υλικού (hardware sprites). Όλα τα sprites είναι επίσης compiled.

ΣΧΟΛΙΑ main.bas

' ============================================================================
' INVASION - Amstrad CPC Edition (mode 0, software sprites)
' ============================================================================
' NTC BASIC. Το παιχνίδι της έκδοσης ZX Spectrum Next, ως έχει, σε έναν CPC: ο κόσμος του, τα landers,
' τα mutants, οι άνθρωποι και το ραντάρ (scanner), οι ήχοι και το gameplay του:
'   - τα 16 μοτίβα sprites (δύο καρέ για το σκάφος, landers, mutants και ανθρώπους, μία εχθρική
'     βολή, μία έκρηξη, το baiter) σχεδιασμένα από το πρόγραμμα (Cpc.cls) από τα γραφικά του INVASION.SPR
'     μετατραπημένα σε mode 0 (CpcData.cls, μέσω του tools/cpcgfx.py): 8 x 16 pixels σε mode 0 το καθένα,
'     στο ίδιο μέγεθος στην οθόνη
'   - αστέρια στο φόντο, με κύλιση πιο αργή από το έδαφος, ένα τοπίο 1024 pixel, το ραντάρ
'     που δείχνει πού βρίσκεται η οθόνη (η οθόνη δείχνει 320 από τα pixel του κόσμου: ένα pixel σε mode 0 ισούται
'     με δύο του Next)
'   - ήχοι για τα πάντα στο AY (Sfx.cls): λέιζερ, εκρήξεις, απαγωγές, διασώσεις, η
'     έξυπνη βόμβα (smart bomb), υπερδιάστημα, η προώθηση
'   - η απώλεια ενός σκάφους δεν επανεκκινεί το κύμα· ακολουθεί μια στιγμή ασφάλειας· το σκάφος στρίβει
'     ομαλά· οι άνθρωποι περπατούν, πέφτουν (από ψηλά πεθαίνουν), πιάνονται και αφήνονται κάτω (500 ο καθένας)·
'     το baiter εμφανίζεται αν ένα κύμα καθυστερεί· ένα επιπλέον σκάφος και έξυπνη βόμβα κάθε 10.000 πόντους· οι
'     άνθρωποι και ο πλανήτης επανέρχονται κάθε πέμπτο κύμα
' Δύο οθόνες (#C000 και #0000· το πρόγραμμα είναι χτισμένο από το #4000)· σε κάθε καρέ το Cpc.Draw τοποθετεί τα
' sprites, το έδαφος και τα αστέρια σε αυτήν που δεν προβάλλεται, και το Cpc.Flip την εμφανίζει.
' Ένας CPC ολοκληρώνει ένα καρέ αυτού σε 25-40 ms, και το Cpc.Flip εμφανίζει ένα κάθε δεύτερο 50ό του   ' <--- ΑΛΛΑΓΗ
' δευτερολέπτου, σταθερά (το Next το έκανε πενήντα φορές το δευτερόλεπτο), οπότε κάθε καρέ εδώ ισοδυναμεί με δύο του Next:   ' <--- ΑΛΛΑΓΗ
' οι ταχύτητες διπλασιάζονται και οι χρόνοι σε καρέ υποδιπλασιάζονται (ο ήχος ενημερώνεται δύο φορές ανά καρέ): ο   ' <--- ΑΛΛΑΓΗ
' ρυθμός του Next.   ' <--- ΑΛΛΑΓΗ


2 «Μου αρέσει»

main.bas

Option Explicit
Option Turbo
Option Org &H4000                 ' η δεύτερη οθόνη είναι #0000-#3FFF (Cpc.cls)· το πρόγραμμα από #4000

' Σταθερές Κόσμου & Οθόνης (η προβολή: 320 x 192 pixel του Next, 160 x 192 mode 0· το y μετρά προς τα πάνω)
Const WORLD_W As Integer = 1024
Const WORLD_MASK As Integer = 1023
Const VIEW_W As Integer = 320
Const PLAY_TOP As Integer = 30
Const PLAY_BOT As Integer = 146

' Δείκτες Μοτίβων Sprite (CpcData.cls, από INVASION.SPR)
Const PAT_PLAYER As Integer = 0
Const PAT_LANDER As Integer = 1
Const PAT_MUTANT As Integer = 2
Const PAT_HUMAN  As Integer = 3
Const PAT_BOLT   As Integer = 4
Const PAT_FRAME2 As Integer = 5      ' + αυτό: το δεύτερο καρέ των 0-3 (σκάφος, lander, mutant, άνθρωπος): μοτίβα 5-8
Const PAT_SHOT   As Integer = 9
Const PAT_BOOM   As Integer = 10     ' 10-13
Const PAT_BAITER As Integer = 14     ' 14-15

' Εκχωρήσεις Θυρίδων Sprite (Cpc.Spr)
Const SLOT_PLAYER As Integer = 0
Const SLOT_LASER0 As Integer = 1
Const SLOT_HUMAN0 As Integer = 5
Const SLOT_ENEMY0 As Integer = 13
Const SLOT_SHOT0  As Integer = 23
Const SLOT_BOOM0  As Integer = 29

' Πλήθη Οντοτήτων
Const MAX_LASERS As Integer = 4
Const MAX_HUMANS As Integer = 8
Const MAX_ENEMIES As Integer = 10
Const MAX_SHOTS As Integer = 6
Const MAX_BOOMS As Integer = 4

' Καταστάσεις Εχθρών
Const ST_DEAD As Integer = 0
Const ST_WANDER As Integer = 1
Const ST_DESCEND As Integer = 2
Const ST_ABDUCT As Integer = 3
Const ST_MUTANT As Integer = 4
Const ST_BAITER As Integer = 5

Const H_DEAD As Integer = 0
Const H_GROUND As Integer = 1
Const H_GRABBED As Integer = 2
Const H_FALLING As Integer = 3
Const H_CARRIED As Integer = 4

' Τι επιστρέφουν τα EnemyStep και HumanStep (Z80)
Const EV_GRABBED As Integer = 1      ' το lander άρπαξε έναν άνθρωπο
Const EV_TAKEN As Integer = 2        ' έβγαλε έναν από την κορυφή: τώρα είναι mutant
Const EV_LASERED As Integer = 3      ' χτυπήθηκε από λέιζερ: ο αριθμός του στα bits 4-5
Const EV_RAMMED As Integer = 4       ' εμβόλισε το σκάφος
Const EV_SHOOT As Integer = 64       ' και πυροβολεί (es_x: η οριζόντια θέση του στην προβολή)
Const EV_SETDOWN As Integer = 1      ' ένας μεταφερόμενος άνθρωπος αφήνεται κάτω
Const EV_CAUGHT As Integer = 2       ' ένας άνθρωπος που πέφτει πιάστηκε
Const EV_FELL As Integer = 3         ' ένας έπεσε από πολύ ψηλά
Const EV_LANDED As Integer = 4       ' ένας έπεσε και προσγειώθηκε
Dim es_x As Integer

' Πένες (Cpc.cls: 0 μαύρο, 1 λευκό, 2 γκρι, 3 κυανό, 4 γαλάζιο, 5 μπλε, 6 κίτρινο, 7 πορτοκαλί,
' 8 κόκκινο, 9 σκούρο κόκκινο, 10 πράσινο, 11 σκούρο πράσινο, 12 λάιμ, 13 μοβ, 14 ροζ, 15 παστέλ κίτρινο)
Const PEN_DIVIDER As Integer = 13
Const PEN_SCANNER As Integer = 10

' Κατάσταση Παίκτη
Dim px As Integer, py As Integer
Dim pvx As Integer, pvy As Integer
Dim pdir As Integer, psx As Integer, cam_x As Integer
Dim thrust As Integer, safeT As Integer, kb As Integer

Dim score As Long, nextBonus As Long
Dim lives As Integer
Dim bombs As Integer
Dim wave As Integer
Dim game_over As Boolean
Dim planet_dead As Boolean
Dim fire_cd As Integer
Dim bomb_cd As Integer
Dim hyper_cd As Integer
Dim flash_bg As Integer
Dim frames As Integer, waveT As Integer, baiterT As Integer, hudDirty As Integer
' για αυτό το καρέ: frames And 1, Mod 3, (\ 4) And 1, (\ 6) And 1 (μια διαίρεση κοστίζει 1.100 κύκλους)
Dim f2 As Integer, f3 As Integer, f4 As Integer, f12 As Integer

' Λέιζερ
Dim lx(MAX_LASERS) As Integer
Dim ly(MAX_LASERS) As Integer
Dim lvx(MAX_LASERS) As Integer
Dim lscr(MAX_LASERS) As Integer, lasersOn As Integer
Dim llife(MAX_LASERS) As Integer

' Ανθρωποειδή
Dim hx(MAX_HUMANS) As Integer
Dim hy(MAX_HUMANS) As Integer
Dim hstate(MAX_HUMANS) As Integer
Dim hcarrier(MAX_HUMANS) As Integer
Dim hdir(MAX_HUMANS) As Integer
Dim hfall(MAX_HUMANS) As Integer, hvy(MAX_HUMANS) As Integer
Dim humans_alive As Integer

' Εχθροί
Dim ex(MAX_ENEMIES) As Integer
Dim ey(MAX_ENEMIES) As Integer
Dim evx(MAX_ENEMIES) As Integer
Dim evy(MAX_ENEMIES) As Integer
Dim estate(MAX_ENEMIES) As Integer
Dim etarget(MAX_ENEMIES) As Integer
Dim enemies_alive As Integer
Dim spawn_timer As Integer
Dim enemies_to_spawn As Integer

' Εχθρικά Βλήματα
Dim sx(MAX_SHOTS) As Integer
Dim sy(MAX_SHOTS) As Integer
Dim svx(MAX_SHOTS) As Integer
Dim svy(MAX_SHOTS) As Integer
Dim slife(MAX_SHOTS) As Integer

' Εκρήξεις (x, y κόσμου, υπολειπόμενα καρέ)
Dim bx(MAX_BOOMS) As Integer, by(MAX_BOOMS) As Integer, bt(MAX_BOOMS) As Integer

' Το τοπίο: ένα ύψος για κάθε 4 pixel του κόσμου (256 συνολικά)
Dim land(255) As Byte
Dim terDead As Boolean

' Οι εντολές τύπου CPC που χρησιμοποιούσε το αρχικό (Pen, At ...)
Dim txtPen As Integer, atx As Integer, aty As Integer
Dim i As Integer, j As Integer, k As Integer, rs As Integer

' ============================================================================
' ΒΟΗΘΗΤΙΚΕΣ ΣΥΝΑΡΤΗΣΕΙΣ SPRITE (οι θυρίδες sprite υλικού του Next, σχεδιασμένες από το Cpc.Draw)
' ============================================================================

' Οι θυρίδες sprite είναι Cpc.Spr slot, x (0..319 κατά πλάτος της προβολής), y (0..191, πάνω), pattern, mirror,
' shown και Cpc.Hide slot - καλούνται απευθείας (μια ενδιάμεση Sub κοστίζει 100 κύκλους ανά κλήση)

' ============================================================================
' ΒΟΗΘΗΤΙΚΕΣ ΣΥΝΑΡΤΗΣΕΙΣ ΣΥΝΤΕΤΑΓΜΕΝΩΝ & ΕΔΑΦΟΥΣ
' ============================================================================

' το τοπίο, δημιουργείται μία φορά: λόφοι και βουνά που ανεβοκατεβαίνουν (όχι πάνω από 4 pixel
' ανά 4 οριζόντια, ώστε η γραμμή να μην έχει κενά)
Sub MakeLand()
    Dim h As Integer, t As Integer
    rs = 12345 : h = 14 : t = 14
    For k = 0 To 255
        If k Mod 16 = 0 Then t = 8 + Rand(23)
        If h < t Then h = h + 1 + Rand(3)
        If h > t Then h = h - 1 - Rand(3)
        If h < 8 Then h = 8
        If h > 32 Then h = 32
        land(k) = h
    Next k
    ' τα τελευταία οδηγούν πίσω στα πρώτα, ώστε να ενώνεται πλήρως κυκλικά
    For k = 248 To 255
        land(k) = land(247) + (land(0) - land(247)) * (k - 247) \ 9
    Next k
End Sub

' Rand(n): 0 έως n - 1 από το rs, το ίδιο κάθε φορά από το ίδιο seed (το τοπίο και τα
' αστέρια δημιουργούνται από αυτό). Μια ακολουθία 16-bit: η αρχική των 32-bit απαιτούσε 10.000 κύκλους ανά εκτέλεση
Function Rand(ByVal n As Integer) As Integer
    rs = (rs * 2053 + 13849) And &H7FFF
    Rand = (rs \ 32) Mod n
End Function

Function GetTerrainY(ByVal wx As Integer) As Integer
    If planet_dead Then GetTerrainY = 6 Else GetTerrainY = land(Cpc.Shr(wx, 2) And 255)
End Function

' Cpc.ScrX(wx, cx): πού βρίσκεται το x του κόσμου wx κατά πλάτος της προβολής από το cx, -512 έως 511.
' Το ραντάρ: τα pixel του Next, 64 + ((wx - px + 512) And 1023) \ 8 κατά πλάτος του πλαισίου (το σκάφος στη
' μέση), 153 + y \ 7 πάνω από το κάτω μέρος: το Cpc.RDot wx, px, y, pen σχεδιάζει μια κουκκίδα εκεί
Function WorldToRadarX(ByVal wx As Integer) As Integer
    WorldToRadarX = 64 + Cpc.Shr((wx - px + 512) And WORLD_MASK, 3)
End Function

Dim hudScore As Long, hudLives As Integer, hudBombs As Integer, hudWave As Integer   ' <--- ΝΕΟ (τι εμφανίζει το HUD: εκτυπώνεται μόνο ό,τι αλλάζει)
Sub UpdateHUD()
    If score <> hudScore Then                                  ' <--- ΝΕΟ
    Pen 15
    At 1, 1
    PrintS Right$("000000" & Mid$(Str$(score), 2), 7)
    hudScore = score                                           ' <--- ΝΕΟ
    End If                                                     ' <--- ΝΕΟ
    If lives <> hudLives Then                                  ' <--- ΝΕΟ
    Pen 14
    At 10, 1
    PrintS "SHIPS" & Str$(lives) & " "
    hudLives = lives                                           ' <--- ΝΕΟ
    End If                                                     ' <--- ΝΕΟ
    If bombs <> hudBombs Then                                  ' <--- ΝΕΟ
    Pen 12
    At 19, 1
    PrintS "BOMBS" & Str$(bombs) & " "
    hudBombs = bombs                                           ' <--- ΝΕΟ
    End If                                                     ' <--- ΝΕΟ
    If wave <> hudWave Then                                    ' <--- ΝΕΟ
    Pen 11
    At 27, 1
    PrintS "WAVE" & Str$(wave) & " "
    hudWave = wave                                             ' <--- ΝΕΟ
    End If                                                     ' <--- ΝΕΟ
    hudDirty = 0
End Sub

Sub DrawStaticFrame()
    ClsAll
    hudScore = -1 : hudLives = -1 : hudBombs = -1 : hudWave = -1   ' <--- ΝΕΟ (η οθόνη έχει καθαριστεί: εκτυπώνεται ολόκληρο το HUD)
    ' Διαχωριστικό σαρωτή (γραμμή 43) και πλαίσιο ραντάρ (τα x 62..193, y 152..182 του Next πάνω από το
    ' κάτω μέρος: pixel 31..96 του CPC, γραμμές 9..39)
    Cpc.HLine 0, 43, 160, PEN_DIVIDER
    Cpc.HLine 31, 9, 66, PEN_SCANNER
    Cpc.HLine 31, 39, 66, PEN_SCANNER
    For k = 10 To 38
        Cpc.HLine 31, k, 1, PEN_SCANNER : Cpc.HLine 96, k, 1, PEN_SCANNER
    Next k
    Stars
    Cpc.Ground 7
    UpdateHUD()
End Sub

' τα αστέρια: διάσπαρτα στον ουρανό (γραμμές 46-155), σχεδιασμένα από το Cpc.Draw με κύλιση στο ένα τέταρτο
' της ταχύτητας του εδάφους
Sub Stars()
    Dim x As Integer, y As Integer
    rs = 777
    For k = 0 To 31
        x = Rand(320) : y = 46 + Rand(110)
        Cpc.Star k, x, y, Val(Mid$("2211", (k Mod 4) + 1, 1))
    Next k
End Sub

' ============================================================================
' ΔΙΑΧΕΙΡΙΣΗ ΚΥΜΑΤΩΝ & ΟΝΤΟΤΗΤΩΝ
' ============================================================================

Sub InitWave()
    Cpc.HideAll
    DrawStaticFrame()

    px = 512 : py = 96 : pvx = 0 : pvy = 0 : pdir = 1 : psx = 110
    safeT = 25

    For i = 0 To MAX_LASERS - 1
        llife(i) = 0
    Next i
    For i = 0 To MAX_SHOTS - 1
        slife(i) = 0
    Next i
    For i = 0 To MAX_BOOMS - 1
        bt(i) = 0
    Next i

    ' οι άνθρωποι: όλοι στην αρχή, και ξανά πίσω κάθε πέμπτο κύμα
    If wave = 1 Or (wave - 1) Mod 5 = 0 Then
        planet_dead = False
        humans_alive = MAX_HUMANS
        For i = 0 To MAX_HUMANS - 1
            hx(i) = (i * 128 + 48) And WORLD_MASK
            hstate(i) = H_GROUND
        Next i
    End If
    For i = 0 To MAX_HUMANS - 1
        hcarrier(i) = -1
        If hstate(i) <> H_DEAD Then
            hstate(i) = H_GROUND
            hy(i) = GetTerrainY(hx(i)) + 6
        End If
        If Rand(2) Then hdir(i) = 1 Else hdir(i) = -1
    Next i

    For i = 0 To MAX_ENEMIES - 1
        estate(i) = ST_DEAD
    Next i

    enemies_to_spawn = 10 + wave * 2
    If enemies_to_spawn > 24 Then enemies_to_spawn = 24
    enemies_alive = 0
    spawn_timer = 5
    waveT = 0 : baiterT = 0

    For i = 0 To 4
        SpawnEnemy()
    Next i
    UpdateHUD()
    Message "WAVE" & Str$(wave), 30
End Sub

Sub SpawnEnemy()
    If enemies_to_spawn <= 0 Then Exit Sub
    For i = 0 To MAX_ENEMIES - 1
        If estate(i) = ST_DEAD Then
            ex(i) = (px + 250 + Rnd(500)) And WORLD_MASK
            ey(i) = 110 + Rnd(30)
            If Rnd(2) = 0 Then evx(i) = 1 Else evx(i) = -1
            evy(i) = -1
            If planet_dead Then estate(i) = ST_MUTANT Else estate(i) = ST_WANDER
            etarget(i) = -1
            enemies_alive = enemies_alive + 1
            enemies_to_spawn = enemies_to_spawn - 1
            Exit Sub
        End If
    Next i
End Sub

' το baiter: ένας γρήγορος ιπτάμενος δίσκος που έρχεται για εσάς όταν ένα κύμα διαρκεί πάρα πολύ
Sub SpawnBaiter()
    For i = 0 To MAX_ENEMIES - 1
        If estate(i) = ST_DEAD Then
            ex(i) = (px + 300) And WORLD_MASK
            ey(i) = py
            estate(i) = ST_BAITER
            evx(i) = 0 : evy(i) = 0
            etarget(i) = -1
            Sfx.Play 1, 40, 2, 13, 25, 0
            Exit Sub
        End If
    Next i
End Sub

Sub FireLaser()
    For i = 0 To MAX_LASERS - 1
        If llife(i) = 0 Then
            lx(i) = (px + pdir * 12) And WORLD_MASK
            ly(i) = py - 1
            lvx(i) = pdir * 32 + pvx
            llife(i) = 7
            Sfx.Play 0, 24, 5, 12, 9, 0
            Exit Sub
        End If
    Next i
End Sub

Sub Boom(ByVal wx As Integer, ByVal wy As Integer)
    For k = 0 To MAX_BOOMS - 1
        If bt(k) = 0 Then
            bx(k) = wx : by(k) = wy : bt(k) = 8
            Exit Sub
        End If
    Next k
End Sub

Sub KillEnemy(ByVal idx As Integer)
    Dim h As Integer
    Cpc.Hide SLOT_ENEMY0 + idx
    Boom ex(idx), ey(idx)

    h = etarget(idx)
    If h >= 0 And h < MAX_HUMANS Then
        If hstate(h) = H_GRABBED And hcarrier(h) = idx Then
            hstate(h) = H_FALLING
            hcarrier(h) = -1
            hfall(h) = hy(h) : hvy(h) = 0
        End If
    End If

    If estate(idx) = ST_BAITER Then
        AddScore 200
    Else
        AddScore 150
        enemies_alive = enemies_alive - 1
    End If
    estate(idx) = ST_DEAD
    Sfx.Play 1, 10, 1, 14, 20, 1
End Sub

Sub AddScore(ByVal n As Integer)
    score = score + n
    If score >= nextBonus Then
        nextBonus = nextBonus + 10000
        lives = lives + 1
        If bombs < 9 Then bombs = bombs + 1
        Sfx.Play 2, 60, -1, 14, 40, 0
    End If
    hudDirty = 1
End Sub

Sub TriggerSmartBomb(ByVal cx As Integer)
    Dim sx_pos As Integer
    If bombs <= 0 Then Exit Sub
    bombs = bombs - 1
    bomb_cd = 12
    Sfx.Play 2, 4, 1, 15, 45, 1
    Border 7
    Cpc.Ink 0, 26                  ' ο ουρανός λευκός
    flash_bg = 2

    For i = 0 To MAX_ENEMIES - 1
        If estate(i) <> ST_DEAD Then
            sx_pos = Cpc.ScrX(ex(i), cx)
            If sx_pos >= -16 And sx_pos <= VIEW_W + 16 Then KillEnemy(i)
        End If
    Next i
    For i = 0 To MAX_SHOTS - 1
        slife(i) = 0
    Next i
    hudDirty = 1
End Sub

' το σκάφος χτυπήθηκε: εκρήγνυται, οι υπόλοιποι απομακρύνονται λίγο, μια στιγμή ασφάλειας, και συνεχίζουμε
Sub LoseLife()
    Dim f As Integer
    Sfx.Play 2, 6, 1, 15, 70, 1
    Sfx.Play 0, 150, 6, 14, 60, 0
    For i = 0 To MAX_HUMANS - 1
        If hstate(i) = H_CARRIED Then
            hstate(i) = H_FALLING : hfall(i) = hy(i) : hvy(i) = 0
        End If
    Next i
    For f = 0 To 29
        Cpc.Spr SLOT_PLAYER, psx, py, PAT_BOOM + (Cpc.Shr(f, 1) And 3), False, (f < 20)
        If (f And 4) = 0 Then Border 2 Else Border 0
        Vsync
    Next f
    Border 0
    lives = lives - 1
    If lives <= 0 Then
        Cpc.HideAll
        game_over = True
        Exit Sub
    End If
    ' οι εχθροί κοντά στο σκάφος απομακρύνονται, οι βολές εξαφανίζονται
    For i = 0 To MAX_ENEMIES - 1
        If estate(i) <> ST_DEAD Then
            If Abs(Cpc.ScrX(ex(i), px)) < 200 Then ex(i) = (ex(i) + 400) And WORLD_MASK
            If estate(i) = ST_BAITER Then
                estate(i) = ST_DEAD : Cpc.Hide SLOT_ENEMY0 + i
            End If
        End If
    Next i
    For i = 0 To MAX_SHOTS - 1
        slife(i) = 0
    Next i
    py = 96 : pvx = 0 : safeT = 50 : baiterT = 0
    hudDirty = 1
End Sub

' ένα μήνυμα στη μέση της περιοχής παιχνιδιού για λίγη ώρα
Dim msgT As Integer, msg As String
Sub Message(ByVal m As String, ByVal t As Integer)
    Pen 14
    At 17 - Cpc.Shr(Len(m), 1), 11                             ' <--- ΑΛΛΑΓΗ (ήταν Len(m) \ 2: μια διαίρεση)
    PrintS m
    msgT = t : msg = m
End Sub

' ============================================================================
' ΤΑ ΜΕΡΗ ΕΝΟΣ ΚΑΡΕ
' ============================================================================

Sub Controls()
    kb = Cpc.Keys()
    ' Πάνω: Q ή Joy Up | Κάτω: A ή Joy Down
    If kb And 1 Then
        If pvy < 6 Then pvy = pvy + 2
    ElseIf kb And 2 Then
        If pvy > -6 Then pvy = pvy - 2
    Else
        pvy = 0
    End If

    ' Αριστερά: O ή Joy Left | Δεξιά: P ή Joy Right
    thrust = 0
    If kb And 4 Then
        pdir = -1 : thrust = 1
        If pvx > -20 Then pvx = pvx - 2
    ElseIf kb And 8 Then
        pdir = 1 : thrust = 1
        If pvx < 20 Then pvx = pvx + 2
    ElseIf f3 <> 0 Then
        If pvx > 0 Then pvx = pvx - 1
        If pvx < 0 Then pvx = pvx + 1
    End If
    If thrust And Sfx.Busy(2) = 0 Then Sfx.Play 2, 28, 0, 5, 3, 1

    px = (px + pvx) And WORLD_MASK
    py = py + pvy
    If py < PLAY_TOP Then py = PLAY_TOP
    If py > PLAY_BOT Then py = PLAY_BOT

    ' το σκάφος κάθεται στη μία πλευρά, αφήνοντας χώρο μπροστά: καθώς στρίβει, γλιστράει κατά πλάτος
    If pdir > 0 And psx > 110 Then psx = psx - 8
    If pdir < 0 And psx < 210 Then psx = psx + 8
    cam_x = (px - psx) And WORLD_MASK

    ' Βολή Λέιζερ: Space, Z, ή Joy Fire 1
    If fire_cd > 0 Then fire_cd = fire_cd - 1
    If (kb And 16) And fire_cd = 0 Then
        FireLaser()
        fire_cd = 3
    End If

    ' Έξυπνη Βόμβα: B, M, ή Joy Fire 2
    If bomb_cd > 0 Then bomb_cd = bomb_cd - 1
    If (kb And 32) And bomb_cd = 0 Then TriggerSmartBomb(cam_x)

    ' Υπερδιάστημα: H: κάπου αλλού, τυχαία
    If hyper_cd > 0 Then hyper_cd = hyper_cd - 1
    If (kb And 64) And hyper_cd = 0 Then
        px = Rnd(WORLD_W)
        py = 50 + Rnd(85)
        pvx = 0
        hyper_cd = 25 : safeT = 12
        Sfx.Play 0, 500, -24, 14, 20, 0
    End If
End Sub

Sub DrawPlayer()
    Dim f As Integer
    f = PAT_PLAYER
    If thrust And f2 Then f = PAT_PLAYER + PAT_FRAME2
    Cpc.Spr SLOT_PLAYER, psx, py, f, (pdir < 0), (safeT = 0 Or (safeT And 2) = 0)
    If safeT > 0 Then safeT = safeT - 1
    Cpc.RDot px, px, py + 7, 1
End Sub

' Λέιζερ: οι βολές κινούνται και σχεδιάζονται (Z80)· lscr και lasersOn για τον έλεγχο πρόσκρουσης των εχθρών
Sub Lasers()
    ' ΑΦΑΙΡΕΘΗΚΕ ΕΝΣΩΜΑΤΩΜΕΝΗ ΓΛΩΣΣΑ ΣΥΜΒΟΛΙΣΜΟΥ
End Sub

Sub Humans()
    ' κάθε άνθρωπος μετακινείται και σχεδιάζεται από το HumanStep (Z80)· ό,τι επιστρέφεται είναι αυτό που πρέπει να
    ' κάνει το παιχνίδι γι' αυτό
    Dim ev As Integer
    i = 0                                                      ' <--- ΝΕΟ
    Do While i < MAX_HUMANS                                    ' <--- ΑΛΛΑΓΗ (ήταν For i = 0 To MAX_HUMANS - 1)
        ev = HumanStep(i)                                      ' <--- ΑΛΛΑΓΗ (Z80: από τον άνθρωπο i και μετά στον πρώτο με εκκρεμή ενέργεια· i = εκείνος ο άνθρωπος)
        If ev Then                                             ' <--- ΝΕΟ (τίποτα προς εκτέλεση: κανένας έλεγχος)
        If ev = EV_SETDOWN Then
            AddScore 500
            Sfx.Play 0, 90, -3, 14, 20, 0
        ElseIf ev = EV_CAUGHT Then
            AddScore 500
            Sfx.Play 0, 140, -6, 14, 14, 0
        ElseIf ev = EV_FELL Then
            ' πολύ μεγάλη πτώση
            Boom hx(i), hy(i)
            Sfx.Play 1, 100, 12, 13, 25, 0
            HumansGone
        ElseIf ev = EV_LANDED Then
            AddScore 250
        End If
        End If                                                 ' <--- ΝΕΟ
        i = i + 1                                              ' <--- ΝΕΟ
    Loop                                                       ' <--- ΑΛΛΑΓΗ (ήταν Next i)
End Sub

' δεν έμειναν άνθρωποι: ο πλανήτης χάνεται, και κάθε lander μετατρέπεται σε mutant
Sub HumansGone()
    If humans_alive <= 0 And Not planet_dead Then
        planet_dead = True
        Border 2
        flash_bg = 5
        Sfx.Play 2, 3, 1, 15, 60, 1
        For k = 0 To MAX_ENEMIES - 1
            If estate(k) = ST_WANDER Or estate(k) = ST_DESCEND Then estate(k) = ST_MUTANT
        Next k
        Message "THE PLANET IS GONE", 50
    End If
End Sub

Sub Enemies()
    Dim ev As Integer, l_idx As Integer

    If spawn_timer > 0 Then
        spawn_timer = spawn_timer - 1
    ElseIf enemies_to_spawn > 0 And enemies_alive < 7 Then
        SpawnEnemy()
        spawn_timer = 30
    End If
    ' το baiter: μετά από 40 δευτερόλεπτα, και έπειτα κάθε 12
    waveT = waveT + 1
    If waveT > 1000 Then
        baiterT = baiterT + 1
        If baiterT >= 300 Or waveT = 1001 Then
            baiterT = 0
            SpawnBaiter()
        End If
    End If

    ' κάθε εχθρός μετακινείται, σχεδιάζεται και ελέγχεται από το EnemyStep (Z80: ήταν 90 λέξεις-κλειδιά ανά εχθρό)·
    ' ό,τι επιστρέφεται είναι αυτό που πρέπει να κάνει το παιχνίδι γι' αυτό
    i = 0                                                      ' <--- ΝΕΟ
    Do While i < MAX_ENEMIES                                   ' <--- ΑΛΛΑΓΗ (ήταν For i = 0 To MAX_ENEMIES - 1)
        ev = EnemyStep(i)                                      ' <--- ΑΛΛΑΓΗ (Z80: από τον εχθρό i και μετά στον πρώτο με εκκρεμή ενέργεια· i = εκείνος ο εχθρός)
        If ev Then
            If (ev And 15) = EV_GRABBED Then
                Sfx.Play 1, 300, -16, 13, 30, 0
                Message "HUMANOID TAKEN!", 25
            ElseIf (ev And 15) = EV_TAKEN Then
                Sfx.Play 1, 60, 8, 15, 40, 0
                HumansGone
            ElseIf (ev And 15) = EV_LASERED Then
                l_idx = Cpc.Shr(ev, 4) And 3
                llife(l_idx) = 0
                Cpc.Hide SLOT_LASER0 + l_idx
                KillEnemy(i)
            ElseIf (ev And 15) = EV_RAMMED Then
                KillEnemy(i)
                LoseLife()
                Exit Sub
            End If
            If ev And EV_SHOOT Then EnemyShot i, es_x
        End If
        i = i + 1                                              ' <--- ΝΕΟ
    Loop                                                       ' <--- ΑΛΛΑΓΗ (ήταν Next i)
End Sub

' EnemyStep(i): το καρέ του εχθρού i - όπως το είχε η Enemies στη BASIC (έκδοση Main.bas του
' ZX Spectrum Next, όπου μπορείτε να δείτε τη λογική): κινείται βάσει της κατάστασής του (περιπλάνηση, κάθοδος για
' άνθρωπο, αρπαγή προς τα πάνω, mutant, το baiter), παραμένει στην περιοχή παιχνιδιού, σχεδιάζεται με την κουκκίδα ραντάρ του
' (cp_showr), και έπειτα ελέγχεται έναντι των λέιζερ και του σκάφους, καθώς και αν πυροβολεί. Επιστρέφει
' με EV_... (0 = τίποτα προς εκτέλεση), es_x = η οριζόντια θέση του στην προβολή
Function EnemyStep(ByVal n As Integer) As Integer   ' <--- ΑΛΛΑΓΗ (n: το {i} παρακάτω είναι το i του παιχνιδιού)
    ' ΑΦΑΙΡΕΘΗΚΕ ΕΝΣΩΜΑΤΩΜΕΝΗ ΓΛΩΣΣΑ ΣΥΜΒΟΛΙΣΜΟΥ
End Function

' HumanStep(i): το καρέ του ανθρώπου i, όπως το είχε η Humans στη BASIC: περπάτημα στο έδαφος, μεταφορά
' από το σκάφος (αφήνεται κάτω όταν συναντήσει το έδαφος), πτώση (πιάσιμο από το σκάφος, ή προσγείωση - από
' πολύ ψηλά, θάνατος)· σχεδιάζεται με την κουκκίδα ραντάρ του. Επιστρέφει με EV_... (0 = τίποτα προς εκτέλεση)
Function HumanStep(ByVal n As Integer) As Integer   ' <--- ΑΛΛΑΓΗ (n: το {i} παρακάτω είναι το i του παιχνιδιού)
    ' ΑΦΑΙΡΕΘΗΚΕ ΕΝΣΩΜΑΤΩΜΕΝΗ ΓΛΩΣΣΑ ΣΥΜΒΟΛΙΣΜΟΥ
End Function


' μια βολή από τον εχθρό n στο screen x esx, στοχευμένη στο σκάφος
Sub EnemyShot(ByVal n As Integer, ByVal esx As Integer)
    Dim dx As Integer, dy As Integer, m As Integer
    For k = 0 To MAX_SHOTS - 1
        If slife(k) = 0 Then
            dx = psx - esx : dy = py - ey(n)
            m = Abs(dx)
            If Abs(dy) > m Then m = Abs(dy)
            If m < 1 Then m = 1
            sx(k) = ex(n) * 4 : sy(k) = ey(n) * 4
            svx(k) = dx * 24 \ m : svy(k) = dy * 24 \ m
            slife(k) = 30
            Sfx.Play 2, 70, -3, 8, 6, 0
            Exit Sub
        End If
    Next k
End Sub

' Shots: οι βολές των εχθρών κινούνται και σχεδιάζονται (Z80)· όποια χτυπήσει το σκάφος το καταστρέφει
Sub Shots()
    If ShotsStep() Then LoseLife()
End Sub

' ShotsStep(): το καρέ των βολών: True όταν κάποια χτύπησε το σκάφος (έχει χαθεί)
Function ShotsStep() As Integer
    ' ΑΦΑΙΡΕΘΗΚΕ ΕΝΣΩΜΑΤΩΜΕΝΗ ΓΛΩΣΣΑ ΣΥΜΒΟΛΙΣΜΟΥ
End Function

' Booms: τα καρέ των εκρήξεων (Z80)
Sub Booms()
    ' ΑΦΑΙΡΕΘΗΚΕ ΕΝΣΩΜΑΤΩΜΕΝΗ ΓΛΩΣΣΑ ΣΥΜΒΟΛΙΣΜΟΥ
End Sub

' τα σημάδια του σαρωτή που δείχνουν το τμήμα του κόσμου στην οθόνη (ακριβώς έξω από το πλαίσιο, πάνω
' και κάτω από αυτό): το Cpc.Draw τα τοποθετεί στο m και 40 pixel ραντάρ μετά
Sub ViewMarks()
    Dim m As Integer
    m = WorldToRadarX(cam_x)
    If m >= 62 And m + 41 <= 194 Then Cpc.Mark m Else Cpc.Mark -1
End Sub

' ============================================================================
' ΚΥΡΙΑ ΕΙΣΟΔΟΣ ΠΡΟΓΡΑΜΜΑΤΟΣ
' ============================================================================

Mode 0
CpcData.Data                      ' (οι πίνακες: δεν εκτελείται ποτέ, τους διατηρεί στο πρόγραμμα)
Cpc.Init
Sfx.Init
Border 0
Pen 15
ClsAll
MakeLand
Heights

Do
    TitleScreen

    score = 0 : nextBonus = 10000
    lives = 3
    bombs = 3
    wave = 1
    game_over = False
    InitWave()

    Do While Not game_over
        Vsync
        frames = frames + 1
        f2 = frames And 1 : f3 = f3 + 1                       ' <--- ΑΛΛΑΓΗ (ήταν f3 = frames Mod 3: μια διαίρεση)
        If f3 = 3 Then f3 = 0                                  ' <--- ΝΕΟ (frames Mod 3 ως μετρητής)
        f4 = Cpc.Shr(frames, 2) And 1 : f12 = Cpc.Div(frames, 6) And 1
        If flash_bg > 0 Then
            flash_bg = flash_bg - 1
            If flash_bg = 0 Then
                Border 0
                Cpc.Ink 0, 0
            End If
        End If
        If msgT > 0 Then
            msgT = msgT - 1
            If msgT = 0 Then
                At 17 - Cpc.Shr(Len(msg), 1), 11 : PrintS Space$(Len(msg))   ' <--- ΑΛΛΑΓΗ (μόνο όπου βρίσκεται το μήνυμα: ήταν At 1, 11 : PrintS Space$(32), 64 γράμματα σχεδιάζονταν)
            ElseIf Cpc.Touched() Then
                Pen 14 : At 17 - Cpc.Shr(Len(msg), 1), 11 : PrintS msg   ' <--- ΑΛΛΑΓΗ (ένα sprite παρέσυρε μέρος του: ξανά· ήταν Message msg, msgT: δύο αντίγραφα της συμβολοσειράς)
            End If
        End If

        Controls
        DrawPlayer
        Lasers
        Humans
        Enemies
        If Not game_over Then Shots
        Booms
        Terrain cam_x
        ViewMarks
        If hudDirty Then UpdateHUD()

        ' ολοκλήρωση κύματος: μπόνους για κάθε άνθρωπο που απέμεινε
        If enemies_alive <= 0 And enemies_to_spawn <= 0 And Not game_over Then
            WaveDone
        End If
    Loop

    ' Οθόνη Game Over
    Cpc.HideAll
    ClsAll
    Cpc.Ground 0
    Pen 10
    At 8, 9
    PrintS "G A M E   O V E R"
    Pen 15
    At 8, 12
    PrintS "FINAL SCORE: " & Mid$(Str$(score), 2)
    Pen 14
    At 8, 15
    PrintS "REACHED WAVE: " & Mid$(Str$(wave), 2)
    For i = 1 To 75
        Vsync
    Next i
Loop

Sub WaveDone()
    Dim b As Integer, f As Integer
    For i = 0 To MAX_ENEMIES - 1
        If estate(i) = ST_BAITER Then estate(i) = ST_DEAD
    Next i
    For i = 0 To MAX_BOOMS - 1
        bt(i) = 0
    Next i
    Cpc.HideAll                ' (τα κείμενα πηγαίνουν εκεί που βρίσκονταν τα sprite)
    b = humans_alive * 100 * wave
    If b > 5000 Then b = 5000
    Pen 14
    At 9, 10 : PrintS "ATTACK WAVE" & Str$(wave)
    At 11, 11 : PrintS "COMPLETED"
    Pen 15
    At 9, 13 : PrintS "BONUS" & Str$(b)
    For f = 0 To 5
        Sfx.Play f Mod 3, 120 - f * 12, 0, 13, 12, 0
        For i = 1 To 5
            Vsync
        Next i
    Next f
    AddScore b
    For i = 1 To 30
        Vsync
    Next i
    wave = wave + 1
    If bombs < 5 Then bombs = bombs + 1
    InitWave()
End Sub

Sub TitleScreen()
    Dim t As Integer, a As Integer
    Cpc.HideAll
    ClsAll
    Stars
    Cpc.Ground 2
    Cpc.Big 16, 6, "INVASION"   ' <--- ΑΛΛΑΓΗ (ήταν DEFENDER)
    Pen 11
    At 7, 6
    PrintS "AMSTRAD CPC EDITION"
    Pen 15
    At 9, 9 : PrintS "LANDER      150"
    At 9, 11 : PrintS "MUTANT      150"
    At 9, 13 : PrintS "BAITER      200"
    At 9, 15 : PrintS "HUMANOID    500"
    Pen 12
    At 3, 17 : PrintS "Q / A or UP/DN : ALTITUDE"
    At 3, 18 : PrintS "O / P or LT/RT : THRUST"
    At 3, 19 : PrintS "SPACE / FIRE 1 : LASER"
    At 3, 20 : PrintS "B / M / FIRE 2 : SMART BOMB"
    At 3, 21 : PrintS "H              : HYPERSPACE"
    Pen 11                                         ' <--- ΝΕΟ
    At 1, 22 : PrintS "(C) 2026 CYBORG UNICORN PTY LTD"   ' <--- ΝΕΟ
    t = 0
    Do
        Vsync
        t = t + 1
        a = Cpc.Shr(t, 2) And 1
        Cpc.Spr 1, 70, 195 - 9 * 8, PAT_LANDER + a * PAT_FRAME2, False, True
        Cpc.Spr 2, 70, 195 - 11 * 8, PAT_MUTANT + a * PAT_FRAME2, False, True
        Cpc.Spr 3, 70, 195 - 13 * 8, PAT_BAITER + a, False, True
        Cpc.Spr 4, 70, 195 - 15 * 8, PAT_HUMAN + a * PAT_FRAME2, False, True
        ' το σκάφος πετάει κατά μήκος του κάτω μέρους
        Cpc.Spr 0, (t * 4) Mod 360 - 20, 6, PAT_PLAYER + a * PAT_FRAME2, False, True
        Pen 10
        At 7, 23
        If Cpc.Div(t, 12) And 1 Then PrintS "PRESS SPACE OR FIRE" Else PrintS "                   "
        k = Rnd(10)
    Loop Until Cpc.Keys() And 16
    Do
        Vsync
    Loop While Cpc.Keys() And 16
    Cpc.HideAll
End Sub

' ============================================================================
' ΟΙ ΕΝΤΟΛΕΣ ΤΥΠΟΥ CPC ΤΟΥ ΑΡΧΙΚΟΥ
' ============================================================================

' Pen n: το χρώμα για τις λέξεις που ακολουθούν (οι αριθμοί πένας του αρχικού, ως πένες Cpc.cls)
Sub Pen(ByVal n As Integer)
    Select Case n
        Case 14: txtPen = 6
        Case 11: txtPen = 3
        Case 10: txtPen = 8
        Case 12: txtPen = 10
        Case 13: txtPen = 14
        Case Else: txtPen = 1
    End Select
End Sub

' Border n: τα αρχικά χρώματα περιγράμματος (0 μαύρο, 2 κόκκινο, 7 λευκό)
Sub Border(ByVal n As Integer)
    Select Case n
        Case 2: Cpc.Ink 16, 6
        Case 7: Cpc.Ink 16, 26
        Case Else: Cpc.Ink 16, 0
    End Select
End Sub

' At x, y: πού τοποθετούνται οι λέξεις (1, 1 = επάνω αριστερά στην εικόνα 32 x 24 του Next: στήλη 4
' από τις 40 του CPC, γραμμή 0 από τις 25 του)
Sub At(ByVal x As Integer, ByVal y As Integer)
    atx = x + 3 : aty = y - 1
End Sub

Sub PrintS(ByVal t As String)
    Cpc.Text atx, aty, t, txtPen
    atx = atx + Len(t)
End Sub

Sub ClsAll()
    Cpc.Cls
End Sub

' Vsync: το καρέ σχεδιάζεται στην οθόνη που δεν εμφανίζεται, εμφανίζεται στην επιστροφή σάρωσης καρέ (frame flyback)· ο ήχος συνεχίζει να παίζει
Sub Vsync()
    Cpc.Draw cam_x
    Cpc.Flip
    Sfx.Update
    Sfx.Update                     ' (δύο φορές: ένα καρέ εδώ ισούται με δύο του Next)
End Sub

' Το έδαφος: το Cpc.Draw το σχεδιάζει από τα ύψη (Heights)· μόνο η καταστροφή του πλανήτη τα αλλάζει
Sub Terrain(ByVal cx As Integer)
    If terDead <> planet_dead Then Heights : terDead = planet_dead
End Sub

' η γραμμή του εδάφους για κάθε x του κόσμου (από πάνω προς τα κάτω), με κλίση ανάμεσα στα βήματα των 4 pixel του
' GetTerrainY
Sub Heights()
    Dim wx As Integer, h0 As Integer, h1 As Integer
    For wx = 0 To WORLD_MASK Step 2
        h0 = GetTerrainY(wx) : h1 = GetTerrainY((wx + 4) And WORLD_MASK)
        Cpc.Height wx, 191 - (h0 + (h1 - h0) * (wx And 3) \ 4)
    Next wx
    terDead = planet_dead
End Sub
1 «Μου αρέσει»

cpc.cls

' Cpc - Η οθόνη του INVASION στο Amstrad CPC: mode 0 (160 x 200, 16 pens), δύο οθόνες (#C000
' και #0000-#3FFF: το πρόγραμμα έχει δομηθεί από το #4000), software sprites που αντικαθιστούν
' τα αντίστοιχα hardware του Next, και η γρήγορη αριθμητική που χρειάζεται ο βρόχος του παιχνιδιού.
' (c) 2026 Cyborg Unicorn Pty Ltd
'
' Σε κάθε frame το παιχνίδι ορίζει τα sprites του (Spr: ένα slot, τη θέση του, το μοτίβο του - όπως στα
' slots του Next, παραμένουν μέχρι να αλλαχθούν· Hide), τις κουκκίδες του ραντάρ (RDot, Dot) και τα σημάδια προβολής (Mark), καθώς και
' τυχόν κείμενο (Text: απευθείας και στις δύο οθόνες), στη συνέχεια το Draw σχεδιάζει το frame στην οθόνη που δεν
' εμφανίζεται: το έδαφος (μόνο όταν μετακινήθηκε η προβολή: τα bytes όπου βρισκόταν την προηγούμενη φορά σβήνονται με μαύρο, και έπειτα ένα   ' <--- CHANGED
' ή δύο pixels ανά στήλη), τα αστέρια (τα παλιά σβήνουν, τα νέα ανάβουν), τις γραμμές που έχει μαυρίσει   ' <--- CHANGED
' ένα sprite, και μετά τα sprites (μεταγλωττισμένος κώδικας). Το Flip το εμφανίζει στη δεύτερη επαναφορά σάρωσης πλαισίου (flyback) από   ' <--- CHANGED
' το τελευταίο flip - 25 frames το δευτερόλεπτο, σταθερά, ανεξάρτητα από το πόσο διήρκεσε ένα frame κάτω από 40 ms (το cp_tick μετρά   ' <--- CHANGED
' τον χρόνο) - και προετοιμάζει την άλλη οθόνη: τα sprites από δύο frames πριν μαυρίζουν, και το   ' <--- CHANGED
' ραντάρ της καθαρίζεται. Τίποτα δεν διατηρείται κάτω από ένα sprite: ό,τι μπορεί να καλυφθεί σχεδιάζεται ξανά,   ' <--- CHANGED
' εκτός από το κείμενο (το Touched αναφέρει πότε επηρεάστηκε η σειρά των μηνυμάτων, και ποια γράμματα).   ' <--- CHANGED
' Οι συντεταγμένες των sprites είναι αυτές του Next (x 0-319 κατά πλάτος της προβολής, y προς τα πάνω από το κάτω μέρος όπως
' στο αρχικό Plot)· ένα μοτίβο 16 x 16 μετατρέπεται σε 8 x 16 pixels σε mode 0 (CpcData.cls).
' Κείμενο: 40 στήλες των 4 pixels (η στενή γραμματοσειρά), 25 σειρές των 8 γραμμών. Οι κλήσεις pixel (Dot, HLine,
' Big) λαμβάνουν pixels mode 0, 0-159 οριζόντια, γραμμές 0-199 κάθετα.
' Όλα αυτά είναι Z80 (#asm)· το μπλοκ του Draw περιέχει τους πίνακες και τις ρουτίνες που καλούν τα υπόλοιπα, επομένως
' ένα πρόγραμμα πρέπει οπωσδήποτε να χρησιμοποιεί το Draw (όλα το κάνουν).
' Διακοπές: IM 2 (ένας πίνακας 257 byte: οποιαδήποτε τιμή διαύλου λειτουργεί), ενεργές μόνο ενώ ο Z80 του Cpc, του Sfx και του παιχνιδιού εκτελείται στην   ' <--- NEW
' πραγματική στοίβα· το νήμα της BASIC (το SP είναι το νήμα) εκτελείται με αυτές απενεργοποιημένες, όπως πριν.   ' <--- NEW
' Μνήμη μετά το πρόγραμμα (δεν περιλαμβάνεται στο αρχείο): οι πίνακες του Text, οι πίνακες γραμμών, οι λίστες του εδάφους και   ' <--- NEW
' των αστεριών (ntc_free και πάνω, περίπου μέχρι το #AD00: η RAM του firmware δεν χρησιμοποιείται αφού έχει εκκινήσει).   ' <--- NEW
Option Explicit

Public Const Slots = 33

' Init: τα 16 μελάνια και το πλαίσιο (border), οι πίνακες γραμμών, η οθόνη στο #C000 εμφανίζεται (η άλλη είναι η
' #0000-#3FFF: το πρόγραμμα έχει δομηθεί από το #4000), τίποτα σχεδιασμένο ακόμα. Μετά το Mode 0
Public Sub Init()
    ' INLINE ASSEMBLY LANGUAGE REMOVED
End Sub

' Ink pen, ink: το χρώμα CPC ink (0-26) μιας πένας (0-15, 16 = το πλαίσιο/border)
Public Sub Ink(ByVal pen As Integer, ByVal ink As Integer)
    ' INLINE ASSEMBLY LANGUAGE REMOVED
End Sub

' Spr slot, x, y, pattern, mirror, shown: το slot 0-32 εμφανίζει το μοτίβο (0-15) με το κέντρο του στο x
' (0-319 κατά πλάτος της προβολής), y (προς τα πάνω από το κάτω μέρος, 0-191), αντικατοπτρισμένο αν mirror· ή κρυφό
Public Sub Spr(ByVal slot As Integer, ByVal x As Integer, ByVal y As Integer, ByVal pat As Integer, ByVal mirror As Integer, ByVal shown As Integer)
    ' INLINE ASSEMBLY LANGUAGE REMOVED
End Sub

' Hide slot: απόκρυψη του slot
Public Sub Hide(ByVal slot As Integer)
    ' INLINE ASSEMBLY LANGUAGE REMOVED
End Sub

' HideAll: απόκρυψη όλων των slots
Public Sub HideAll()
    ' INLINE ASSEMBLY LANGUAGE REMOVED
End Sub

' Text col, row, s, pen: το s από τη στήλη col (0-39), σειρά row (0-24) με πένα pen (μαύρο από πίσω του), και στις δύο
' οθόνες. Χαρακτήρες 32-95 (τα πεζά γράμματα εμφανίζονται ως κεφαλαία). Ένα sprite πάνω από το κείμενο το παρασύρει
' όταν φεύγει (το Flip μαυρίζει εκεί που βρισκόταν): το Touched αναφέρει πότε συνέβη αυτό στη
' σειρά μηνυμάτων
Public Sub Text(ByVal col As Integer, ByVal row As Integer, ByVal s As String, ByVal pen As Integer)
    ' INLINE ASSEMBLY LANGUAGE REMOVED
End Sub

' Touched(): True μία φορά όταν ένα sprite έχει μαυριστεί από τη σειρά μηνυμάτων (γραμμές 80-87)
' σε οποιαδήποτε οθόνη από την τελευταία κλήση: το μήνυμα χρειάζεται εκ νέου εκτύπωση (και το Text που
' το εκτυπώνει σχεδιάζει στη συνέχεια μόνο τα γράμματα που μαυρίστηκαν, σε κάθε οθόνη)   ' <--- CHANGED
Public Function Touched() As Integer
    ' INLINE ASSEMBLY LANGUAGE REMOVED
End Function

' Dot x, y, pen: μια κουκκίδα ραντάρ (pixel x 0-159, γραμμή y 0-199) στην οθόνη που σχεδιάζεται (το
' εσωτερικό του ραντάρ καθαρίζεται σε κάθε Flip)
Public Sub Dot(ByVal x As Integer, ByVal y As Integer, ByVal pen As Integer)
    ' INLINE ASSEMBLY LANGUAGE REMOVED
End Sub

' RDot wx, px, y, pen: η κουκκίδα ραντάρ για x κόσμου wx (το σκάφος στο px βρίσκεται στη μέση του
' πλαισίου), y (προς τα πάνω από το κάτω μέρος): στο pixel 32 + ((wx - px + 512) And 1023) \ 16, γραμμή 38 - y \ 7
Public Sub RDot(ByVal wx As Integer, ByVal px As Integer, ByVal y As Integer, ByVal pen As Integer)
    ' INLINE ASSEMBLY LANGUAGE REMOVED
End Sub

' Show(slot, wx, y, pattern, mirror, cx, px, pen): μια οντότητα του κόσμου στο wx, y (προς τα πάνω από
' το κάτω μέρος) με μία κίνηση: το sprite της στο slot (εμφανίζεται εκεί όπου βρίσκεται στην προβολή από το cx όταν αυτό είναι
' -8 έως 327, αλλιώς κρυφό), η κουκκίδα ραντάρ της με πένα pen (καμία όταν το pen είναι -1), και επιστρέφει τη θέση της
' κατά πλάτος της προβολής (-512 έως 511). Μία λέξη-κλειδί για ό,τι απαιτούσε δέκα. (Ο Z80 στο παιχνίδι
' γεμίζει το cp_shb και καλεί το cp_showr: το ίδιο πράγμα, χωρίς λέξη-κλειδί)
Public Function Show(ByVal slot As Integer, ByVal wx As Integer, ByVal y As Integer, ByVal pat As Integer, ByVal mirror As Integer, ByVal cx As Integer, ByVal px As Integer, ByVal pen As Integer) As Integer
    ' INLINE ASSEMBLY LANGUAGE REMOVED
End Function

' ScrX(wx, cx): πού βρίσκεται το x κόσμου wx κατά πλάτος μιας προβολής από το cx: -512 έως 511
Public Function ScrX(ByVal wx As Integer, ByVal cx As Integer) As Integer
    ' INLINE ASSEMBLY LANGUAGE REMOVED
End Function

' Mark x: τα σημάδια προβολής στο ραντάρ x (του Next: 62-153), στην οθόνη που σχεδιάζεται: ένα byte
' (δύο λευκά pixels) στο x / 4 και 10 bytes μετά, γραμμές 7 και 41 (αφαιρούνται στο επόμενο Flip, όπως
' και οι κουκκίδες). -1 = κανένα
Public Sub Mark(ByVal x As Integer)
    ' INLINE ASSEMBLY LANGUAGE REMOVED
End Sub

' Star k, x, y, pen: αστέρι k (0-31) στο x (0-319 στον ουρανό), γραμμή y, με πένα pen. (Διατηρείται ως x,
' η μετατόπιση της γραμμής σε μια οθόνη και τα bits των αριστερών pixel της πένας: τίποτα προς αναζήτηση κατά τη σχεδίαση)
Public Sub Star(ByVal k As Integer, ByVal x As Integer, ByVal y As Integer, ByVal pen As Integer)
    ' INLINE ASSEMBLY LANGUAGE REMOVED
End Sub

' Height wx, y: η γραμμή του εδάφους (0-199) στο x κόσμου wx (0-1023, ζυγοί αριθμοί: οι μονοί δεν διατηρούνται)
Public Sub Height(ByVal wx As Integer, ByVal y As Integer)
    ' INLINE ASSEMBLY LANGUAGE REMOVED
End Sub

' GroundAt(wx): το ύψος του εδάφους (προς τα πάνω από το κάτω μέρος) στο x κόσμου wx, από τον πίνακα υψών
' (Height), σε 100 κύκλους - το GetTerrainY του παιχνιδιού χρειαζόταν 500
Public Function GroundAt(ByVal wx As Integer) As Integer
    ' INLINE ASSEMBLY LANGUAGE REMOVED
End Function

' Ground on: τι σχεδιάζει το Draw εκτός από τα sprites: 1 το έδαφος, 2 τα αστέρια, 4 το ραντάρ (το
' εσωτερικό του καθαρίζεται σε κάθε Flip, οι γραμμές του σχεδιάζονται ξανά). Προστέθηκαν: 7 κατά το παιχνίδι, 2 στον τίτλο, 0 στην
' οθόνη game over
Public Sub Ground(ByVal on As Integer)
    ' INLINE ASSEMBLY LANGUAGE REMOVED
End Sub

' Cls: και οι δύο οθόνες μαύρες, καμία διατήρηση δεδομένων σε καμία
Public Sub Cls()
    ' INLINE ASSEMBLY LANGUAGE REMOVED
End Sub

' HLine x, y, w, pen: μια οριζόντια γραμμή από το pixel x, γραμμή y, πλάτους w pixels, και στις δύο οθόνες
Public Sub HLine(ByVal x As Integer, ByVal y As Integer, ByVal w As Integer, ByVal pen As Integer)
    ' INLINE ASSEMBLY LANGUAGE REMOVED
End Sub

' Big x, y, s: το s με τους ενσωματωμένους χαρακτήρες 8 x 8 του CPC, δύο pixels πλάτος και τρεις γραμμές ύψος για καθένα
' από τα δικά τους (16 x 24), με διαβάθμιση από παστέλ κίτρινο σε κόκκινο και σκούρα κόκκινη σκιά, από το pixel x (ζυγό),
' γραμμή y, και στις δύο οθόνες
Public Sub Big(ByVal x As Integer, ByVal y As Integer, ByVal s As String)
    ' INLINE ASSEMBLY LANGUAGE REMOVED
End Sub

' Flip: αναμονή για τη δεύτερη επαναφορά σάρωσης πλαισίου (flyback) από το τελευταίο flip (την επόμενη όταν αυτό έχει ήδη
' παρέλθει), εμφάνιση της οθόνης στην οποία σχεδίασε το Draw, σχεδίαση στην άλλη από εδώ και πέρα   ' <--- CHANGED
Public Sub Flip()
    ' INLINE ASSEMBLY LANGUAGE REMOVED
End Sub

' Shr(x, n): x (0-65535, ως unsigned) μετατοπισμένο δεξιά κατά n bits: x \ 2, 4, 8 ... χωρίς τη διαίρεση
' (η οποία διαρκεί περίπου 1.100 κύκλους· αυτή διαρκεί 100)
Public Function Shr(ByVal x As Integer, ByVal n As Integer) As Integer
    ' INLINE ASSEMBLY LANGUAGE REMOVED
End Function

' Div(x, d): x \ d για x 0-65535 (unsigned), d 1-255: περίπου 400 κύκλοι (η διαίρεση παίρνει 1.100),
' 250 για x κάτω από 256
Public Function Div(ByVal x As Integer, ByVal d As Integer) As Integer
    ' INLINE ASSEMBLY LANGUAGE REMOVED
End Function

' Rnd(n): ένας τυχαίος αριθμός από 0 έως n - 1 (n 1-255), σε περίπου 350 κύκλους (το Rnd του NTC χρειάζεται 1.200)
Public Function Rnd(ByVal n As Integer) As Integer
    ' INLINE ASSEMBLY LANGUAGE REMOVED
End Function

' Keys(): τα πατημένα πλήκτρα: 1 πάνω (Q, joystick πάνω), 2 κάτω (A), 4 αριστερά (O), 8 δεξιά (P), 16 βολή
' (space, Z, fire 1), 32 smart bomb (B, M, fire 2), 64 hyperspace (H)
Public Function Keys() As Integer
    ' INLINE ASSEMBLY LANGUAGE REMOVED
End Function

' Draw cx: το frame στην οθόνη όπου γίνεται η σχεδίαση (αυτή που δεν εμφανίζεται: το Flip αφαίρεσε τα παλιά της
' sprites και καθάρισε το ραντάρ της), η προβολή από το x κόσμου cx: το έδαφος και τα αστέρια
' (Ground), τα sprites. Στη συνέχεια Flip
Public Sub Draw(ByVal cx As Integer)
    ' INLINE ASSEMBLY LANGUAGE REMOVED
End Sub

sfx.cls

' Sfx - ηχητικά εφέ στο τσιπ AY του CPC, μέσω του PPI (η θύρα A #F4xx μεταφέρει τα δεδομένα,
' η θύρα C #F6xx τα bit ελέγχου του AY). Τρία κανάλια, το καθένα παίζει ένα εφέ τη φορά:
' έναν τόνο ή θόρυβο του οποίου το τονικό ύψος ολισθαίνει και η ένταση εξασθενεί στο μηδέν κατά τη διάρκειά του.
' (c) 2026 Cyborg Unicorn Pty Ltd
' Κλήση του Update μία φορά κάθε 1/50 του δευτερολέπτου· εδώ το Vsync το καλεί δύο φορές ανά καρέ, και η εγγραφή στο AY γίνεται
' στη δεύτερη από κάθε δύο (οι εγγραφές της πρώτης αντικαθίσταντο αμέσως)   ' <--- ΑΛΛΑΓΜΕΝΟ
'   Sfx.Play 0, 200, 8, 15, 12, 0     ' κανάλι A: περίοδος τόνου 200, ανοδικό τονικό ύψος... 12 καρέ
Option Explicit

Private per(2) As Integer         ' η τρέχουσα περίοδος τόνου (12 bit· θόρυβος: 0-31)
Private slide(2) As Integer       ' προστίθεται σε αυτή σε κάθε καρέ
Private vol(2) As Integer         ' η ένταση με την οποία ξεκινά (0-15)
Private total(2) As Integer       ' η διάρκειά του σε καρέ
Private left(2) As Integer        ' καρέ που απομένουν (0 = σιωπή)
Private nse(2) As Integer         ' 1 = θόρυβος, 0 = τόνος

Private Sub W(ByVal r As Integer, ByVal v As Integer)
    ' ΑΦΑΙΡΕΘΗΚΕ ΕΝΣΩΜΑΤΩΜΕΝΗ ΓΛΩΣΣΑ ΣΥΜΒΟΛΙΣΜΟΥ (INLINE ASSEMBLY)
End Sub

' Init: σίγαση σε κάθε κανάλι
Public Sub Init()
  Dim i As Integer
  For i = 0 To 13
    W i, 0
  Next
  W 7, &H3F
  For i = 0 To 2
    left(i) = 0
  Next
End Sub

' Play ch, period, slidePerFrame, volume, frames, noise: έναρξη εφέ στο κανάλι ch (0-2)
Public Sub Play(ByVal ch As Integer, ByVal period As Integer, ByVal sl As Integer, ByVal volume As Integer, ByVal frames As Integer, ByVal noise As Integer)
  If ch < 0 Or ch > 2 Then Exit Sub
  per(ch) = period : slide(ch) = sl : vol(ch) = volume : total(ch) = frames : left(ch) = frames : nse(ch) = noise
End Sub

' Busy(ch): True όσο παίζει το κανάλι ch
Public Function Busy(ByVal ch As Integer) As Integer
  Busy = (left(ch) > 0)
End Function

' Update: μία φορά ανά καρέ - τα τονικά ύψη ολισθαίνουν, οι εντάσεις εξασθενούν, γίνεται εγγραφή στο AY (Z80: καλείται
' δύο φορές ανά καρέ)
Public Sub Update()
    ' ΑΦΑΙΡΕΘΗΚΕ ΕΝΣΩΜΑΤΩΜΕΝΗ ΓΛΩΣΣΑ ΣΥΜΒΟΛΙΣΜΟΥ (INLINE ASSEMBLY)
End Sub

' Quiet: διακοπή σε κάθε κανάλι
Public Sub Quiet()
  Dim i As Integer
  For i = 0 To 2
    left(i) = 0
    W 8 + i, 0
  Next
End Sub

και τέλος cpcdata.cls

' CpcData - τα sprite και η στενή γραμματοσειρά του INVASION για τον Amstrad CPC, mode 0 (δημιουργήθηκε από το
' tools/cpcgfx.py από το INVASION.SPR: μην επεξεργαστείτε). Τα sprite είναι μεταγλωττισμένα: καθένα από τα 16
' μοτίβα, απλά και κατοπτρικά (μόνο το σκάφος, οι άνθρωποι και ο κεραυνός έχουν κατοπτρικό:
' οι καταχωρίσεις των υπόλοιπων δείχνουν στο απλό), ως κώδικας Z80 που το σχεδιάζει στο HL (το πάνω αριστερό
' byte του) - ένα ολόκληρο byte γράφεται, ένα μισό εφαρμόζεται με μάσκα, ένα κενό παραλείπεται - από τη μία γραμμή στην
' επόμενη κατά #800 (cp_nextw μετά το όγδοο μιας σειράς: Cpc.cls). Το cd_spr είναι ένας πίνακας 32 καταχωρίσεων (μοτίβο * 2 + κατοπτρικό): xoff,
' yoff, w (byte), h, η διεύθυνση του κώδικα. Τα sprite σχεδιάζονται σε άρτια pixel (όριο byte).
' cd_font: 64 χαρακτήρες (32-95) των 8 σειρών, μια σειρά 4 pixel στα bit 7-4. cd_inks: οι αριθμοί μελανιών CPC
' των 16 πενών.
' (c) 2026 Cyborg Unicorn Pty Ltd
Option Explicit

' Data: δεν καλείται ποτέ - περιέχει τους πίνακες και τον κώδικα των sprite (καθολικές ετικέτες, που χρησιμοποιούνται από το Cpc.cls)
Public Sub Data()
' ΑΦΑΙΡΕΘΗΚΕ ΕΝΣΩΜΑΤΩΜΕΝΗ ΓΛΩΣΣΑ ΣΥΜΒΟΛΙΣΜΟΥ (INLINE ASSEMBLY)
End Sub

Σε τι μεταφράζεται (assemble) ο παραπάνω κώδικας; Ορίστε ένα απόσπασμα.

; ============================================================
; NTC program - generated by NTC BASIC (VB style BASIC to NTC)
; platform: cpc (Amstrad CPC - direct hardware, no firmware), starts in turbo mode
; ============================================================

			DEVICE AMSTRADCPC6128		; for SAVEAMSDOS at the end
			org #4000

NTC_TERM	equ 0		; Option Terminal: 0 none, 1 VT52, 2 ANSI (CP/M)
NTC_STRMAX	equ 40		; the longest string (library buffers, temps)
NTC_MEMBANKS	equ 64		; Option Memory: the most 64K banks the Memory class looks for
NTC_ROMENABLE	equ 0		; Option RomEnable (cpcfw, cpcmin): the start code starts the ROMs
NTC_ROMLIB	equ 0		; 1 = the library is the keyword ROM (Library: ROM; ntcRomAsm builds it)
NTC_WINHOLE	equ 0		; 1 = the program is built round an Option Reserve range over the window #4000-#7FFF
NTC_BUFSIZE	equ 2048		; the library's buffer (ntc_buf: FileSystem, Memory.Copy): Option Buffer
NTC_CPMGFX	equ 0		; 1 = CP/M: Graphics draws on an Amstrad CPC's screen (2.2: #C000, Plus: block 1 from common memory)

ntc_start:
			di
			ld (ntc_csp), sp
			ld sp, main_thread
			ret

; ============================================================
; Main Thread
; ============================================================

main_thread:
			dw kw_bssinit, ntc_bss, ntc_bss_size		; the arrays: do they fit, clear them

			; Main.bas 1975: Mode 0
			dw kw_model, 0

			; Main.bas 1976: CpcData.Data                      ' (the tables: never runs, keeps them in the program)
			dw fn_cpcdata_data

			; Main.bas 1977: Cpc.Init
			dw fn_cpc_init

			; Main.bas 1978: Sfx.Init
			dw p_sfx_init_c

			; Main.bas 1979: Border 0
			dw p_border_c, lit_0

			; Main.bas 1980: Pen 15
			dw p_pen_c, lit_15

			; Main.bas 1981: ClsAll
			dw p_clsall_c

			; Main.bas 1982: MakeLand
			dw p_makeland_c

			; Main.bas 1983: Heights
			dw p_heights_c

			; Main.bas 1985: Do
do1_t:

			; Main.bas 1986: TitleScreen
			dw p_titlescreen_c

			; Main.bas 1988: score = 0 : nextBonus = 10000
			dw kw_l_mov, llit_0, var_score
			dw kw_l_mov, llit_10000, var_nextbonus

			; Main.bas 1989: lives = 3
			dw kw_ltov, 3, var_lives

			; Main.bas 1990: bombs = 3
			dw kw_ltov, 3, var_bombs

			; Main.bas 1991: wave = 1
			dw kw_ltov, 1, var_wave

			; Main.bas 1992: game_over = False
			dw kw_ztov, var_game_over

			; Main.bas 1993: InitWave()
			dw p_initwave_c

			; Main.bas 1995: Do While Not game_over
do2_t:
			dw kw_notv, var_game_over, tmp0
			dw kw_goto_ifzero, tmp0, do2_e

			; Main.bas 1996: Vsync
			dw p_vsync_c

			; Main.bas 1997: frames = frames + 1
			dw kw_inc, var_frames

			; Main.bas 1998: f2 = frames And 1 : f3 = f3 + 1                       ' <--- CHANGED (was f3 = frames Mod 3: a division)
			dw kw_andvl, var_frames, 1, var_f2
			dw kw_inc, var_f3

			; Main.bas 1999: If f3 = 3 Then f3 = 0                                  ' <--- NEW (frames Mod 3 as a counter)
			dw kw_cpeqvl, var_f3, 3
			dw kw_goto_iffalse, if3_1
			dw kw_ztov, var_f3
if3_1:

			; Main.bas 2000: f4 = Cpc.Shr(frames, 2) And 1 : f12 = Cpc.Div(frames, 6) And 1
			dw fn_cpc_shr, var_frames, lit_2, var_cpc_shr_shr
			dw kw_vtov, var_cpc_shr_shr, tmp0
			dw kw_andvl, tmp0, 1, var_f4
			dw fn_cpc_div, var_frames, lit_6, var_cpc_div_div
			dw kw_vtov, var_cpc_div_div, tmp0
			dw kw_andvl, tmp0, 1, var_f12

			; Main.bas 2001: If flash_bg > 0 Then
			dw kw_scpgtvl, var_flash_bg, 0
			dw kw_goto_iffalse, if4_1

			; Main.bas 2002: flash_bg = flash_bg - 1
			dw kw_dec, var_flash_bg

			; Main.bas 2003: If flash_bg = 0 Then
			dw kw_cpeqvl, var_flash_bg, 0
			dw kw_goto_iffalse, if5_1

			; Main.bas 2004: Border 0
			dw p_border_c, lit_0

			; Main.bas 2005: Cpc.Ink 0, 0
			dw fn_cpc_ink, lit_0, lit_0

			; Main.bas 2006: End If
if5_1:

			; Main.bas 2007: End If
if4_1:

			; Main.bas 2008: If msgT > 0 Then
			dw kw_scpgtvl, var_msgt, 0
			dw kw_goto_iffalse, if6_1

			; Main.bas 2009: msgT = msgT - 1
			dw kw_dec, var_msgt

			; Main.bas 2010: If msgT = 0 Then
			dw kw_cpeqvl, var_msgt, 0
			dw kw_goto_iffalse, if7_1

			; Main.bas 2011: At 17 - Cpc.Shr(Len(msg), 1), 11 : PrintS Space$(Len(msg))   ' <--- CHANGED (only where the message is: was At 1, 11 : PrintS Space$(32), 64 letters drawn)
			dw kw_strlen, var_msg, tmp1
			dw fn_cpc_shr, tmp1, lit_1, var_cpc_shr_shr
			dw kw_vtov, var_cpc_shr_shr, tmp1
			dw kw_sublv, 17, tmp1, tmp0
			dw p_at_c, tmp0, lit_11
			dw kw_strlen, var_msg, tmp0
			dw kw_strspace, stmp0, tmp0
			dw p_prints_c, stmp0

			; Main.bas 2012: ElseIf Cpc.Touched() Then
			dw if7_e_go
if7_1:
			dw fn_cpc_touched, var_cpc_touched_touched
			dw kw_goto_ifzero, var_cpc_touched_touched, if7_2

			; Main.bas 2013: Pen 14 : At 17 - Cpc.Shr(Len(msg), 1), 11 : PrintS msg   ' <--- CHANGED (a sprite took some of it with it: again; was Message msg, msgT: two copies of the string)
			dw p_pen_c, lit_14
			dw kw_strlen, var_msg, tmp1
			dw fn_cpc_shr, tmp1, lit_1, var_cpc_shr_shr
			dw kw_vtov, var_cpc_shr_shr, tmp1
			dw kw_sublv, 17, tmp1, tmp0
			dw p_at_c, tmp0, lit_11
			dw p_prints_c, var_msg

			; Main.bas 2014: End If
if7_2:
if7_e:

			; Main.bas 2015: End If
if6_1:

			; Main.bas 2017: Controls
			dw p_controls_c

			; Main.bas 2018: DrawPlayer
			dw p_drawplayer_c

			; Main.bas 2019: Lasers
			dw fn_lasers

			; Main.bas 2020: Humans
			dw p_humans_c

			; Main.bas 2021: Enemies
			dw p_enemies_c

			; Main.bas 2022: If Not game_over Then Shots
			dw kw_notv, var_game_over, tmp0
			dw kw_goto_ifzero, tmp0, if8_1
			dw p_shots_c
if8_1:

			; Main.bas 2023: Booms
			dw fn_booms

			; Main.bas 2024: Terrain cam_x
			dw p_terrain_c, var_cam_x

			; Main.bas 2025: ViewMarks
			dw p_viewmarks_c

			; Main.bas 2026: If hudDirty Then UpdateHUD()
			dw kw_goto_ifzero, var_huddirty, if9_1
			dw p_updatehud_c
if9_1:

			; Main.bas 2029: If enemies_alive <= 0 And enemies_to_spawn <= 0 And Not game_over Then
			dw kw_scplevl, var_enemies_alive, 0
			dw kw_ftov, tmp0
			dw kw_scplevl, var_enemies_to_spawn, 0
			dw kw_ftov, tmp1
			dw kw_andvv, tmp0, tmp1, tmp0
			dw kw_notv, var_game_over, tmp1
			dw kw_andvv, tmp0, tmp1, tmp0
			dw kw_goto_ifzero, tmp0, do2_t

			; Main.bas 2030: WaveDone
			dw p_wavedone_c

			; Main.bas 2031: End If
if10_1:

			; Main.bas 2032: Loop
			dw do2_t_go
do2_e:

			; Main.bas 2035: Cpc.HideAll
			dw fn_cpc_hideall

			; Main.bas 2036: ClsAll
			dw p_clsall_c

			; Main.bas 2037: Cpc.Ground 0
			dw fn_cpc_ground, lit_0

			; Main.bas 2038: Pen 10
			dw p_pen_c, lit_10

			; Main.bas 2039: At 8, 9
			dw p_at_c, lit_8, lit_9

			; Main.bas 2040: PrintS "G A M E   O V E R"
			dw p_prints_c, str0

			; Main.bas 2041: Pen 15
			dw p_pen_c, lit_15

			; Main.bas 2042: At 8, 12
			dw p_at_c, lit_8, lit_12

			; Main.bas 2043: PrintS "FINAL SCORE: " & Mid$(Str$(score), 2)
			dw kw_l_str, stmp1, var_score
			dw kw_strmid, stmp1, stmp1, lit_2, lit_255
			dw kw_strcpy, stmp0, str1
			dw kw_strcat, stmp0, stmp1
			dw p_prints_c, stmp0

			; Main.bas 2044: Pen 14
			dw p_pen_c, lit_14

			; Main.bas 2045: At 8, 15
			dw p_at_c, lit_8, lit_15

			; Main.bas 2046: PrintS "REACHED WAVE: " & Mid$(Str$(wave), 2)
			dw kw_strstr, stmp1, var_wave
			dw kw_strmid, stmp1, stmp1, lit_2, lit_255
			dw kw_strcpy, stmp0, str2
			dw kw_strcat, stmp0, stmp1
			dw p_prints_c, stmp0

			; Main.bas 2047: For i = 1 To 75
			dw kw_ltov, 1, var_i
for11_t:

			; Main.bas 2048: Vsync
			dw p_vsync_c

			; Main.bas 2049: Next i
			dw kw_next1l, var_i, 75, for11_t
for11_e:

			; Main.bas 2050: Loop
			dw do1_t_go
do1_e:
			dw kw_stop

; ============================================================
; sub makeland (turbo mode)
; ============================================================

p_makeland:

			; Main.bas 172: Dim h As Integer, t As Integer

			; Main.bas 173: rs = 12345 : h = 14 : t = 14
			dw kw_ltov, 12345, var_rs
			dw kw_ltov, 14, var_makeland_h
			dw kw_ltov, 14, var_makeland_t

			; Main.bas 174: For k = 0 To 255
			dw kw_ztov, var_k
for12_t:

			; Main.bas 175: If k Mod 16 = 0 Then t = 8 + Rand(23)
			dw kw_modvl, var_k, 16, tmp0
			dw kw_cpeqvl, tmp0, 0
			dw kw_goto_iffalse, if13_1
			dw p_rand_c, lit_23
			dw kw_vtov, var_rand_rand, tmp1
			dw kw_addl, tmp1, 8, var_makeland_t

if13_1:

			; Main.bas 176: If h < t Then h = h + 1 + Rand(3)
			dw kw_scpltvv, var_makeland_h, var_makeland_t
			dw kw_goto_iffalse, if14_1
			dw kw_addl, var_makeland_h, 1, tmp0
			dw p_rand_c, lit_3
			dw kw_vtov, var_rand_rand, tmp1
			dw kw_add, tmp0, tmp1, var_makeland_h
if14_1:

			; Main.bas 177: If h > t Then h = h - 1 - Rand(3)
			dw kw_scpgtvv, var_makeland_h, var_makeland_t
			dw kw_goto_iffalse, if15_1
			dw kw_addl, var_makeland_h, -1, tmp0
			dw p_rand_c, lit_3
			dw kw_vtov, var_rand_rand, tmp1
			dw kw_subvv, tmp0, tmp1, var_makeland_h
if15_1:

			; Main.bas 178: If h < 8 Then h = 8
			dw kw_scpltvl, var_makeland_h, 8
			dw kw_goto_iffalse, if16_1
			dw kw_ltov, 8, var_makeland_h
if16_1:

			; Main.bas 179: If h > 32 Then h = 32
			dw kw_scpgtvl, var_makeland_h, 32
			dw kw_goto_iffalse, if17_1
			dw kw_ltov, 32, var_makeland_h
if17_1:

			; Main.bas 180: land(k) = h
			dw kw_aset8, arr_land, var_k, var_makeland_h

			; Main.bas 181: Next k
			dw kw_next1l, var_k, 255, for12_t
for12_e:

			; Main.bas 183: For k = 248 To 255
			dw kw_ltov, 248, var_k
for18_t:

			; Main.bas 184: land(k) = land(247) + (land(0) - land(247)) * (k - 247) \ 9
			dw kw_lptrtov8, arr_land+247, tmp1
			dw kw_lptrtov8, arr_land, tmp2
			dw kw_lptrtov8, arr_land+247, tmp3
			dw kw_subvv, tmp2, tmp3, tmp2
			dw kw_addl, var_k, -247, tmp3
			dw kw_mulvv, tmp2, tmp3, tmp2
			dw kw_divvl, tmp2, 9, tmp2
			dw kw_add, tmp1, tmp2, tmp1
			dw kw_aset8, arr_land, var_k, tmp1

			; Main.bas 185: Next k
			dw kw_next1l, var_k, 255, for18_t
for18_e:
			dw p_makeland_r

; ============================================================
; function rand (turbo mode)
; ============================================================

p_rand:

			; Main.bas 191: rs = (rs * 2053 + 13849) And &H7FFF
			dw kw_mulvl, var_rs, 2053, tmp1
			dw kw_addl, tmp1, 13849, tmp1
			dw kw_andvl, tmp1, 32767, var_rs

			; Main.bas 192: Rand = (rs \ 32) Mod n
			dw kw_divvl, var_rs, 32, tmp1
			dw kw_modvv, tmp1, var_rand_n, var_rand_rand
			dw p_rand_r

; ============================================================
; function getterrainy (turbo mode)
; ============================================================

p_getterrainy:

			; Main.bas 196: If planet_dead Then GetTerrainY = 6 Else GetTerrainY = land(Cpc.Shr(wx, 2) And 255)
			dw kw_goto_ifzero, var_planet_dead, if19_1
			dw kw_ltov, 6, var_getterrainy_getterrainy
			dw if19_e_go
if19_1:
			dw fn_cpc_shr, var_getterrainy_wx, lit_2, var_cpc_shr_shr
			dw kw_vtov, var_cpc_shr_shr, tmp1
			dw kw_andvl, tmp1, 255, tmp1
			dw kw_aget8, arr_land, tmp1, var_getterrainy_getterrainy
if19_e:
			dw p_getterrainy_r

; ============================================================
; function worldtoradarx (turbo mode)
; ============================================================

p_worldtoradarx:

			; Main.bas 203: WorldToRadarX = 64 + Cpc.Shr((wx - px + 512) And WORLD_MASK, 3)
			dw kw_subvv, var_worldtoradarx_wx, var_px, tmp1
			dw kw_addl, tmp1, 512, tmp1
			dw kw_andvl, tmp1, 1023, tmp1
			dw fn_cpc_shr, tmp1, lit_3, var_cpc_shr_shr
			dw kw_vtov, var_cpc_shr_shr, tmp1
			dw kw_addl, tmp1, 64, var_worldtoradarx_worldtoradarx
			dw p_worldtoradarx_r

; ============================================================
; sub updatehud (turbo mode)
; ============================================================

p_updatehud:

			; Main.bas 208: If score <> hudScore Then                                  ' <--- NEW
			dw kw_l_cpne, var_score, var_hudscore
			dw kw_goto_iffalse, if20_1

			; Main.bas 209: Pen 15
			dw p_pen_c, lit_15

			; Main.bas 210: At 1, 1
			dw p_at_c, lit_1, lit_1

			; Main.bas 211: PrintS Right$("000000" & Mid$(Str$(score), 2), 7)
			dw kw_l_str, stmp1, var_score
			dw kw_strmid, stmp1, stmp1, lit_2, lit_255
			dw kw_strcpy, stmp0, str3
			dw kw_strcat, stmp0, stmp1
			dw kw_strright, stmp0, stmp0, lit_7
			dw p_prints_c, stmp0

			; Main.bas 212: hudScore = score                                           ' <--- NEW
			dw kw_l_mov, var_score, var_hudscore

			; Main.bas 213: End If                                                     ' <--- NEW
if20_1:

			; Main.bas 214: If lives <> hudLives Then                                  ' <--- NEW
			dw kw_cpnevv, var_lives, var_hudlives
			dw kw_goto_iffalse, if21_1

			; Main.bas 215: Pen 14
			dw p_pen_c, lit_14

			; Main.bas 216: At 10, 1
			dw p_at_c, lit_10, lit_1

			; Main.bas 217: PrintS "SHIPS" & Str$(lives) & " "
			dw kw_strstr, stmp1, var_lives
			dw kw_strcpy, stmp0, str4
			dw kw_strcat, stmp0, stmp1
			dw kw_strcat, stmp0, str5
			dw p_prints_c, stmp0

			; Main.bas 218: hudLives = lives                                           ' <--- NEW
			dw kw_vtov, var_lives, var_hudlives

			; Main.bas 219: End If                                                     ' <--- NEW
if21_1:

			; Main.bas 220: If bombs <> hudBombs Then                                  ' <--- NEW
			dw kw_cpnevv, var_bombs, var_hudbombs
			dw kw_goto_iffalse, if22_1

			; Main.bas 221: Pen 12
			dw p_pen_c, lit_12

			; Main.bas 222: At 19, 1
			dw p_at_c, lit_19, lit_1

			; Main.bas 223: PrintS "BOMBS" & Str$(bombs) & " "
			dw kw_strstr, stmp1, var_bombs
			dw kw_strcpy, stmp0, str6
			dw kw_strcat, stmp0, stmp1
			dw kw_strcat, stmp0, str5
			dw p_prints_c, stmp0

			; Main.bas 224: hudBombs = bombs                                           ' <--- NEW
			dw kw_vtov, var_bombs, var_hudbombs

			; Main.bas 225: End If                                                     ' <--- NEW
if22_1:

			; Main.bas 226: If wave <> hudWave Then                                    ' <--- NEW
			dw kw_cpnevv, var_wave, var_hudwave
			dw kw_goto_iffalse, if23_1

			; Main.bas 227: Pen 11
			dw p_pen_c, lit_11

			; Main.bas 228: At 27, 1
			dw p_at_c, lit_27, lit_1

			; Main.bas 229: PrintS "WAVE" & Str$(wave) & " "
			dw kw_strstr, stmp1, var_wave
			dw kw_strcpy, stmp0, str7
			dw kw_strcat, stmp0, stmp1
			dw kw_strcat, stmp0, str5
			dw p_prints_c, stmp0

			; Main.bas 230: hudWave = wave                                             ' <--- NEW
			dw kw_vtov, var_wave, var_hudwave

			; Main.bas 231: End If                                                     ' <--- NEW
if23_1:

			; Main.bas 232: hudDirty = 0
			dw kw_ztov, var_huddirty
			dw p_updatehud_r

; ============================================================
; sub drawstaticframe (turbo mode)
; ============================================================

p_drawstaticframe:

			; Main.bas 236: ClsAll
			dw p_clsall_c

			; Main.bas 237: hudScore = -1 : hudLives = -1 : hudBombs = -1 : hudWave = -1   ' <--- NEW (the screen is clear: all of the HUD printed)
			dw kw_l_mov, llit_m1, var_hudscore
			dw kw_ltov, -1, var_hudlives
			dw kw_ltov, -1, var_hudbombs
			dw kw_ltov, -1, var_hudwave

			; Main.bas 240: Cpc.HLine 0, 43, 160, PEN_DIVIDER
			dw fn_cpc_hline, lit_0, lit_43, lit_160, lit_13

			; Main.bas 241: Cpc.HLine 31, 9, 66, PEN_SCANNER
			dw fn_cpc_hline, lit_31, lit_9, lit_66, lit_10

			; Main.bas 242: Cpc.HLine 31, 39, 66, PEN_SCANNER
			dw fn_cpc_hline, lit_31, lit_39, lit_66, lit_10

			; Main.bas 243: For k = 10 To 38
			dw kw_ltov, 10, var_k
for24_t:

			; Main.bas 244: Cpc.HLine 31, k, 1, PEN_SCANNER : Cpc.HLine 96, k, 1, PEN_SCANNER
			dw fn_cpc_hline, lit_31, var_k, lit_1, lit_10
			dw fn_cpc_hline, lit_96, var_k, lit_1, lit_10

			; Main.bas 245: Next k
			dw kw_next1l, var_k, 38, for24_t
for24_e:

			; Main.bas 246: Stars
			dw p_stars_c

			; Main.bas 247: Cpc.Ground 7
			dw fn_cpc_ground, lit_7

			; Main.bas 248: UpdateHUD()
			dw p_updatehud_c
			dw p_drawstaticframe_r

; ============================================================
; sub stars (turbo mode)
; ============================================================

p_stars:

			; Main.bas 254: Dim x As Integer, y As Integer

			; Main.bas 255: rs = 777
			dw kw_ltov, 777, var_rs

			; Main.bas 256: For k = 0 To 31
			dw kw_ztov, var_k
for25_t:

			; Main.bas 257: x = Rand(320) : y = 46 + Rand(110)
			dw p_rand_c, lit_320
			dw kw_vtov, var_rand_rand, var_stars_x
			dw p_rand_c, lit_110
			dw kw_vtov, var_rand_rand, tmp1
			dw kw_addl, tmp1, 46, var_stars_y

			; Main.bas 258: Cpc.Star k, x, y, Val(Mid$("2211", (k Mod 4) + 1, 1))
			dw kw_modvl, var_k, 4, tmp4
			dw kw_inc, tmp4
			dw kw_strmid, stmp3, str8, tmp4, lit_1
			dw kw_strval, stmp3, tmp3
			dw fn_cpc_star, var_k, var_stars_x, var_stars_y, tmp3

			; Main.bas 259: Next k
			dw kw_next1l, var_k, 31, for25_t
for25_e:
			dw p_stars_r

; ============================================================
; sub initwave (turbo mode)
; ============================================================

p_initwave:

			; Main.bas 267: Cpc.HideAll
			dw fn_cpc_hideall

			; Main.bas 268: DrawStaticFrame()
			dw p_drawstaticframe_c

			; Main.bas 270: px = 512 : py = 96 : pvx = 0 : pvy = 0 : pdir = 1 : psx = 110
			dw kw_ltov, 512, var_px
			dw kw_ltov, 96, var_py
			dw kw_ztov, var_pvx
			dw kw_ztov, var_pvy
			dw kw_ltov, 1, var_pdir
			dw kw_ltov, 110, var_psx

			; Main.bas 271: safeT = 25
			dw kw_ltov, 25, var_safet

			; Main.bas 273: For i = 0 To MAX_LASERS - 1
			dw kw_ztov, var_i
for26_t:

			; Main.bas 274: llife(i) = 0
			dw kw_asetl, arr_llife, var_i, 0

			; Main.bas 275: Next i
			dw kw_next1l, var_i, 3, for26_t
for26_e:

			; Main.bas 276: For i = 0 To MAX_SHOTS - 1
			dw kw_ztov, var_i
for27_t:

			; Main.bas 277: slife(i) = 0
			dw kw_asetl, arr_slife, var_i, 0

			; Main.bas 278: Next i
			dw kw_next1l, var_i, 5, for27_t
for27_e:

			; Main.bas 279: For i = 0 To MAX_BOOMS - 1
			dw kw_ztov, var_i
for28_t:

			; Main.bas 280: bt(i) = 0
			dw kw_asetl, arr_bt, var_i, 0

			; Main.bas 281: Next i
			dw kw_next1l, var_i, 3, for28_t
for28_e:

			; Main.bas 284: If wave = 1 Or (wave - 1) Mod 5 = 0 Then
			dw kw_cpeqvl, var_wave, 1
			dw kw_ftov, tmp0
			dw kw_addl, var_wave, -1, tmp1
			dw kw_modvl, tmp1, 5, tmp1
			dw kw_cpeqvl, tmp1, 0
			dw kw_ftov, tmp1
			dw kw_orvv, tmp0, tmp1, tmp0
			dw kw_goto_ifzero, tmp0, if29_1

			; Main.bas 285: planet_dead = False
			dw kw_ztov, var_planet_dead

			; Main.bas 286: humans_alive = MAX_HUMANS
			dw kw_ltov, 8, var_humans_alive

			; Main.bas 287: For i = 0 To MAX_HUMANS - 1
			dw kw_ztov, var_i
for30_t:

			; Main.bas 288: hx(i) = (i * 128 + 48) And WORLD_MASK
			dw kw_mulvl, var_i, 128, tmp1
			dw kw_addl, tmp1, 48, tmp1
			dw kw_andvl, tmp1, 1023, tmp1
			dw kw_aset, arr_hx, var_i, tmp1

			; Main.bas 289: hstate(i) = H_GROUND
			dw kw_asetl, arr_hstate, var_i, 1

			; Main.bas 290: Next i
			dw kw_next1l, var_i, 7, for30_t
for30_e:

			; Main.bas 291: End If
if29_1:

			; Main.bas 292: For i = 0 To MAX_HUMANS - 1
			dw kw_ztov, var_i
for31_t:

			; Main.bas 293: hcarrier(i) = -1
			dw kw_asetl, arr_hcarrier, var_i, -1

			; Main.bas 294: If hstate(i) <> H_DEAD Then
			dw kw_aget, arr_hstate, var_i, tmp0
			dw kw_cpnevl, tmp0, 0
			dw kw_goto_iffalse, if32_1

			; Main.bas 295: hstate(i) = H_GROUND
			dw kw_asetl, arr_hstate, var_i, 1

			; Main.bas 296: hy(i) = GetTerrainY(hx(i)) + 6
			dw kw_aget, arr_hx, var_i, tmp1
			dw p_getterrainy_c, tmp1
			dw kw_vtov, var_getterrainy_getterrainy, tmp1
			dw kw_addl, tmp1, 6, tmp1
			dw kw_aset, arr_hy, var_i, tmp1

			; Main.bas 297: End If
if32_1:

			; Main.bas 298: If Rand(2) Then hdir(i) = 1 Else hdir(i) = -1
			dw p_rand_c, lit_2
			dw kw_goto_ifzero, var_rand_rand, if33_1
			dw kw_asetl, arr_hdir, var_i, 1
			dw if33_e_go
if33_1:
			dw kw_asetl, arr_hdir, var_i, -1
if33_e:

			; Main.bas 299: Next i
			dw kw_next1l, var_i, 7, for31_t
for31_e:

			; Main.bas 301: For i = 0 To MAX_ENEMIES - 1
			dw kw_ztov, var_i
for34_t:

			; Main.bas 302: estate(i) = ST_DEAD
			dw kw_asetl, arr_estate, var_i, 0

			; Main.bas 303: Next i
			dw kw_next1l, var_i, 9, for34_t
for34_e:

			; Main.bas 305: enemies_to_spawn = 10 + wave * 2
			dw kw_2x, var_wave, tmp1
			dw kw_addl, tmp1, 10, var_enemies_to_spawn

			; Main.bas 306: If enemies_to_spawn > 24 Then enemies_to_spawn = 24
			dw kw_scpgtvl, var_enemies_to_spawn, 24
			dw kw_goto_iffalse, if35_1
			dw kw_ltov, 24, var_enemies_to_spawn
if35_1:

			; Main.bas 307: enemies_alive = 0
			dw kw_ztov, var_enemies_alive

			; Main.bas 308: spawn_timer = 5
			dw kw_ltov, 5, var_spawn_timer

			; Main.bas 309: waveT = 0 : baiterT = 0
			dw kw_ztov, var_wavet
			dw kw_ztov, var_baitert

			; Main.bas 311: For i = 0 To 4
			dw kw_ztov, var_i
for36_t:

			; Main.bas 312: SpawnEnemy()
			dw p_spawnenemy_c

			; Main.bas 313: Next i
			dw kw_next1l, var_i, 4, for36_t
for36_e:

			; Main.bas 314: UpdateHUD()
			dw p_updatehud_c

			; Main.bas 315: Message "WAVE" & Str$(wave), 30
			dw kw_strstr, stmp1, var_wave
			dw kw_strcpy, stmp0, str7
			dw kw_strcat, stmp0, stmp1
			dw p_message_c, stmp0, lit_30
			dw p_initwave_r

; ============================================================
; sub spawnenemy (turbo mode)
; ============================================================

p_spawnenemy:

			; Main.bas 319: If enemies_to_spawn <= 0 Then Exit Sub
			dw kw_scplevl, var_enemies_to_spawn, 0
			dw kw_goto_iffalse, if37_1
			dw p_spawnenemy_r
if37_1:

			; Main.bas 320: For i = 0 To MAX_ENEMIES - 1
			dw kw_ztov, var_i
for38_t:

			; Main.bas 321: If estate(i) = ST_DEAD Then
			dw kw_aget, arr_estate, var_i, tmp0
			dw kw_cpeqvl, tmp0, 0
			dw kw_goto_iffalse, if39_1

			; Main.bas 322: ex(i) = (px + 250 + Rnd(500)) And WORLD_MASK
			dw kw_addl, var_px, 250, tmp1
			dw kw_rndl, 500, tmp2
			dw kw_add, tmp1, tmp2, tmp1
			dw kw_andvl, tmp1, 1023, tmp1
			dw kw_aset, arr_ex, var_i, tmp1

			; Main.bas 323: ey(i) = 110 + Rnd(30)
			dw kw_rndl, 30, tmp2
			dw kw_addl, tmp2, 110, tmp1
			dw kw_aset, arr_ey, var_i, tmp1

			; Main.bas 324: If Rnd(2) = 0 Then evx(i) = 1 Else evx(i) = -1
			dw kw_rndl, 2, tmp0
			dw kw_cpeqvl, tmp0, 0
			dw kw_goto_iffalse, if40_1
			dw kw_asetl, arr_evx, var_i, 1
			dw if40_e_go
if40_1:
			dw kw_asetl, arr_evx, var_i, -1
if40_e:

			; Main.bas 325: evy(i) = -1
			dw kw_asetl, arr_evy, var_i, -1

			; Main.bas 326: If planet_dead Then estate(i) = ST_MUTANT Else estate(i) = ST_WANDER
			dw kw_goto_ifzero, var_planet_dead, if41_1
			dw kw_asetl, arr_estate, var_i, 4
			dw if41_e_go
if41_1:
			dw kw_asetl, arr_estate, var_i, 1
if41_e:

			; Main.bas 327: etarget(i) = -1
			dw kw_asetl, arr_etarget, var_i, -1

			; Main.bas 328: enemies_alive = enemies_alive + 1
			dw kw_inc, var_enemies_alive

			; Main.bas 329: enemies_to_spawn = enemies_to_spawn - 1
			dw kw_dec, var_enemies_to_spawn

			; Main.bas 330: Exit Sub
			dw p_spawnenemy_r

			; Main.bas 331: End If
if39_1:

			; Main.bas 332: Next i
			dw kw_next1l, var_i, 9, for38_t
for38_e:
			dw p_spawnenemy_r

; ============================================================
; sub spawnbaiter (turbo mode)
; ============================================================

p_spawnbaiter:

			; Main.bas 337: For i = 0 To MAX_ENEMIES - 1
			dw kw_ztov, var_i
for42_t:

			; Main.bas 338: If estate(i) = ST_DEAD Then
			dw kw_aget, arr_estate, var_i, tmp0
			dw kw_cpeqvl, tmp0, 0
			dw kw_goto_iffalse, if43_1

			; Main.bas 339: ex(i) = (px + 300) And WORLD_MASK
			dw kw_addl, var_px, 300, tmp1
			dw kw_andvl, tmp1, 1023, tmp1
			dw kw_aset, arr_ex, var_i, tmp1

			; Main.bas 340: ey(i) = py
			dw kw_aset, arr_ey, var_i, var_py

			; Main.bas 341: estate(i) = ST_BAITER
			dw kw_asetl, arr_estate, var_i, 5

			; Main.bas 342: evx(i) = 0 : evy(i) = 0
			dw kw_asetl, arr_evx, var_i, 0
			dw kw_asetl, arr_evy, var_i, 0

			; Main.bas 343: etarget(i) = -1
			dw kw_asetl, arr_etarget, var_i, -1

			; Main.bas 344: Sfx.Play 1, 40, 2, 13, 25, 0
			dw p_sfx_play_c, lit_1, lit_40, lit_2, lit_13, lit_25, lit_0

			; Main.bas 345: Exit Sub
			dw p_spawnbaiter_r

			; Main.bas 346: End If
if43_1:

			; Main.bas 347: Next i
			dw kw_next1l, var_i, 9, for42_t
for42_e:
			dw p_spawnbaiter_r

Όπως μπορείτε να δείτε, το πρόγραμμα μοιάζει με δεδομένα — είναι και δεν είναι. Ο SP επαναχρησιμοποιείται ως PC — πράγμα που σημαίνει ότι το πρόγραμμα μπορεί να μεταπηδά απευθείας από τη μία λέξη-κλειδί στην επόμενη χωρίς επιβαρύνσεις (overhead) κλήσεων και μεταβίβασης παραμέτρων. Αυτός είναι ο λόγος για τον οποίο τόσο στο ZX Spectrum Next όσο και στο Amstrad CPC μπορούν να φτιαχτούν τόσο γρήγορα παιχνίδια που εξαντλούν τις δυνατότητες του υλικού (ειδικά στο ZX Spectrum Next) από τη BASIC, επειδή η BASIC ταιριάζει σε αυτή τη δομή. C; Ως συντακτική ζάχαρη (syntactic sugar) ναι, αλλά όχι για το πώς λειτουργεί συνήθως στα παρασκήνια. Η NTC BASIC σάς επιτρέπει να προγραμματίζετε σε JavaScript (υποσύνολο ES5+) ή PHP (υποσύνολο) αν δεν σας αρέσει το στυλ της Visual BASIC 5/6. Αυτό λειτουργεί μεταφράζοντας τη JavaScript ή την PHP σε NTC BASIC μέσω ενός προεπεξεργαστή πριν από τη μεταγλώττιση — πράγμα που σημαίνει προβλέψιμη συμπεριφορά και ένα μόνο σύνολο σφαλμάτων μεταγλώττισης (compile bugs). Όλες οι μεταβλητές είναι static χωρίς εξαίρεση — γιατί έχει σημασία αυτό; Χάνετε την αναδρομή — αλλά κερδίζετε σταθερότητα — δεν υπάρχει σωρός (heap). Μπορείτε να φτιάξετε ένα heap; Φυσικά και μπορείτε, αλλά είναι προτιμότερο να δημιουργήσετε συναρτήσεις για να διαχειριστείτε τη μνήμη σας όπως επιθυμείτε.

Το CyborgOS (ο hypervisor για Z80) είναι επίσης κωδικοποιημένο σε NTC BASIC (με αρκετή inline Assembly). Όλος ο κώδικας βρίσκεται στο repo του CyborgOS για τα builds των CPC, ZX Spectrum Next και MSX.

Οι κλάσεις (classes) υπάρχουν στην NTC BASIC για να περιέχουν σχετικές συναρτήσεις ως συντακτική ευκολία, αλλά και για να διασφαλίζουν ότι συγκρατούνται ώστε να παραμένουν εντός ενός bank. Αυτό επιτρέπει τον έλεγχο του χάρτη μνήμης (memory map) για μεγάλα προγράμματα έως και 4 megabyte! Ή, όπως συμβαίνει με το CyborgOS — 20 στιγμιότυπα (instances) προγραμμάτων CP/M να εκτελούνται ταυτόχρονα, έχοντας ακόμη περισσότερη από τη μισή RAM ελεύθερη σε μηχανήματα των 4MB. Αυτή η επιπλέον RAM θα μπορούσε να χρησιμοποιηθεί για περισσότερα instances, αλλά… χρησιμοποιείται για προγράμματα οδήγησης (drivers), για «το heap» που περιέχεται μέσα σε μια κλάση Linked List, ramdiscs κ.λπ. — μπορείτε να δημιουργείτε και να καταργείτε ramdiscs εν πτήσει (on the fly). Κάθε CP/M instance αγνοεί τα υπόλοιπα instances — και δεν χρειάζεται να είναι απαραίτητα CP/M — θα μπορούσαν κάλλιστα να είναι ένα POSIX shell, κάτι που εξετάζω. Οι κλάσεις μπορούν να καθοδηγούνται από διακοπές (interrupt driven) και να ενεργοποιούνται στα δικά τους banks — όταν μια εφαρμογή χρειάζεται πολλά δεδομένα, τα ζητάει, και ο πυρήνας (kernel), ο οποίος μπορεί να βρίσκεται στο C0 ή στο C2 (έχει δοκιμαστεί, αλλά προς το παρόν χρησιμοποιείται το C0), τα παρέχει μαζικά σαν ένα εικονικό DMA (είναι DMA μόνο από την οπτική της εφαρμογής).

Για μη αναβαθμισμένα 464; Μπορείτε να κερδίσετε περίπου άλλα 12kb χρησιμοποιώντας τις ROM της NTC BASIC που περιέχουν τις λέξεις-κλειδιά — αλλά σοβαρά τώρα; Πηγαίνετε να αγοράσετε μια επέκταση μνήμης — το 1mb κάνει θαύματα στον υπολογιστή σας! Τα 4mb είναι ό,τι θα χρειαστείτε ποτέ.

Είναι η NTC BASIC τόσο γρήγορη όσο ο SDCC; Όχι — και στην πραγματικότητα ούτε ο SDCC είναι ταχύτερος — εξαρτάται από το τι κάνετε. Η NTC BASIC δεν είναι ένας ιδιαίτερα βελτιστοποιητικός μεταγλωττιστής (optimising compiler) — γίνονται μόνο μερικές βασικές βελτιστοποιήσεις. Αλλά προσφέρει τόσα πολλά που ο SDCC δεν δίνει — όπως η πλήρης αξιοποίηση των overlays καθώς και των σελίδων (pages) (η λεπτή διαφορά είναι ότι τα overlays αντικαθιστούν το ένα το άλλο για υπολογιστές χωρίς πολλή RAM — ή instances του CP/M που δεν θέλουν να χρησιμοποιήσουν νεότερες τεχνικές), ενώ οι σελίδες παραμένουν σε banks μνήμης.

1 «Μου αρέσει»