Μικρά μαθήματα

03 / ΜΟΙΡΑΖΟΝΤΑΣ ΤΟΝ ΕΠΕΞΕΡΓΑΣΤΗ

Αφήστε την BASIC να τρέξει.
Δώστε σειρά στην ML.

Μια εφαρμογή αποκλειστικά σε γλώσσα μηχανής μπορεί να έχει ατέρμονο βρόχο. Το AIS4CBM χρειάζεται να λειτουργεί και το περιβάλλον BASIC. Η λύση είναι να δανειστούμε τον βρόχο εντολών της μέσω του διανύσματος IGONE.

Commodore 128 · BASIC 7.0 + assembly 8502 · Ο χρονοπρογραμματισμός πίσω από το μάθημα WAIT

01 / ΣΕ ΠΟΙΟΝ ΑΝΗΚΕΙ Ο ΒΡΟΧΟΣ;

Ένας ατέρμονος βρόχος ML αφήνει την BASIC να περιμένει.

Σε εφαρμογή μόνο γλώσσας μηχανής, ο κύριος βρόχος θα αποκωδικοποιούσε μηνύματα, θα ενημέρωνε την οθόνη και θα επαναλαμβανόταν. Οι IRQ και NMI θα διέκοπταν την εργασία, θα εξυπηρετούσαν το υλικό και θα επέστρεφαν στον κώδικα. Η εφαρμογή θα συνέχιζε στον δικό της βρόχο.

Το δικό μας περιβάλλον είναι γραμμένο σε BASIC. Αν η BASIC μπει σε ατέρμονη ρουτίνα ML, δεν επιστρέφει ποτέ στην επόμενη εντολή της. Οι διακοπές συνεχίζουν τη δουλειά τους, αλλά μενού, σχεδίαση και χειριστές BASIC σταματούν να προχωρούν.

Μια απλή εναλλακτική είναι ένας βρόχος ελέγχου σε BASIC: έλεγχος σημαιών με PEEK και IF, κλήση υπηρεσίας ML με SYSκαι επανάληψη. Ακόμη και χωρίς χρήσιμη εργασία, ο διερμηνευτής θα εκτελούσε ελέγχους, κλήσεις και διακλαδώσεις. Αυτό το κόστος επαναλαμβανόμενης διερμηνείας θέλουμε να αποφύγουμε.

Στόχος μας είναι να περιορίσουμε την εργασία του διερμηνευτή μεταξύ χρήσιμων συμβάντων. Εδώ η WAIT αλλάζει τον σχεδιασμό: η BASIC αξιολογεί διεύθυνση και μάσκες μία φορά, και η ROM επαναλαμβάνει τον έλεγχο σημαιών σε γλώσσα μηχανής. Οι ενεργοί χειριστές διακοπών ενημερώνουν σημαίες και απελευθερώνουν την αναμονή. Ο επεξεργαστής συνεχίζει τον έλεγχο, αλλά χωρίς επανειλημμένη διερμηνεία βρόχου BASIC.

Έπειτα χρησιμοποιούμε την IGONE για να δίνουμε χρόνο εκτέλεσης στον πυρήνα ML. Ανακατευθύνοντας το διάνυσμα διανομής εντολών BASIC, εκτελούμε ένα πέρασμα υπηρεσίας ML κάθε φορά που φτάνει εκεί ο διερμηνευτής και μετά συνεχίζουμε την BASIC. Η εξυπηρέτηση ML γίνεται μέρος της υπάρχουσας διαδρομής, ακόμη και μέσα στους χειριστές του περιβάλλοντος.

A WAIT-και-μετά-SYS θα απέφευγε επίσης τον συνεχή έλεγχο σε BASIC. Το πρόσθετο όφελος της IGONE είναι η αυτόματη εξυπηρέτηση όσο προχωρά η BASIC, χωρίς ρητές κλήσεις υπηρεσίας σε όλο το περιβάλλον. Όσο η WAIT περιμένει, παύει και η IGONE· οι αφυπνίσεις από διακοπές επαναφέρουν τις εντολές και την παρέμβαση ML.

02 / ΔΑΝΕΙΣΤΕΙΤΕ ΤΟΝ ΒΡΟΧΟ ΤΟΥ ΔΙΕΡΜΗΝΕΥΤΗ

Η IGONE είναι το σημείο παράδοσης.

Η διαδρομή διανομής εντολών BASIC του C128 περνά μέσω της IGONE, ενός διανύσματος δύο byte στη $0308–$0309. Κανονικά οδηγεί στη $4AA2 της ROM BASIC. Το AIS4CBM αποθηκεύει τον τρέχοντα στόχο και ανακατευθύνει το διάνυσμα στη mainLoop.

Παρά το όνομά της, αυτή η mainLoop εκτελεί ένα πέρασμα υπηρεσίας ανά κλήση. Καλεί την ais4cbmManager.main, επαναφέρει καταχωρητές και διαμόρφωση μνήμης όπως παρακάτω, και συνεχίζει προς τον αποθηκευμένο στόχο IGONE. Η BASIC διανέμει την επόμενη εντολή της.

Ολοκληρώνεται εντολή BASIC, ο διερμηνευτής φτάνει στην IGONE, εκτελείται ένα πέρασμα ML και ο αποθηκευμένος διανομέας εκτελεί την επόμενη εντολή BASIC. Οι ενεργές IRQ και NMI μπορούν να παρέμβουν.
Ο διερμηνευτής δίνει την επανάληψη. Η παρέμβαση ML δίνει ένα πέρασμα υπηρεσίας και επιστρέφει τον έλεγχο. Πλήρες μέγεθος ↗

Πρόκειται για συνεργατικό χρονοπρογραμματισμό στα σημεία διανομής εντολών. Δεν υπάρχει σταθερό χρονικό κβάντο: μεγάλη εντολή BASIC καθυστερεί την ML και μεγάλη εργασία ML καθυστερεί την BASIC. Οι IRQ/NMI παραμένουν ανεξάρτητες διακοπές.

03 / ΕΓΚΑΤΑΣΤΑΣΗ, ΕΞΥΠΗΡΕΤΗΣΗ, ΣΥΝΕΧΕΙΑ

Κρατήστε μια διαδρομή πίσω στην BASIC.

Η εγκατάσταση αποθηκεύει το προηγούμενο διάνυσμα πριν ενεργοποιήσει τη δική μας παρέμβαση. IGONE_VEC ορίζεται ως $0308:

Basic8Config.asm · απόσπασμα αντικατάστασης διανύσματος
!install:
        lda IGONE_VEC
        sta oldGoneLo
        lda IGONE_VEC+1
        sta oldGoneHi

        lda #<mainLoop
        sta IGONE_VEC
        lda #>mainLoop
        sta IGONE_VEC+1
!done:
        rts

Η πλήρης εγκατάσταση ανιχνεύει ήδη ενεργή παρέμβαση και διορθώνει κενό ή αναδρομικό αποθηκευμένο στόχο. Έτσι η επανεγκατάσταση δεν συνδέει τη ρουτίνα με τον εαυτό της. Η εκκίνηση καλεί την εγκατάσταση με αποκλεισμένες IRQ.

Η πλήρης ροή ελέγχου της παρέμβασης

Η τρέχουσα γέφυρα, χωρίς σχόλια και με απλοποιημένα κενά. oldGoneLo και oldGoneHi είναι γειτονικά byte που κρατούν τον προηγούμενο στόχο:

irqbridges.asm · mainLoop
mainLoop: {
        pha
        txa
        pha
        tya
        pha

        lda $ff00
        sta originalBank+1

        lda #BANK0
        and #%11001110
        sta $FF00
        jsr ais4cbmManager.main

        lda #BANK15
        sta $ff00
        lda #0
        sta $d507

originalBank:
        lda #$00
        sta $FF00

        pla
        tay
        pla
        tax
        pla

        jmp (oldGoneLo)
}

Η στοίβα διατηρεί A, X και Y. Η γέφυρα αποθηκεύει τη διαμόρφωση MMU, επιλέγει τη χαρτογράφηση ML και καλεί την υπηρεσία. Έπειτα κάνει ορατό το I/O, επαναφέρει την πραγματική μηδενική σελίδα για την BASIC, την αποθηκευμένη MMU και τους τρεις καταχωρητές.

Η τελική JMP (oldGoneLo) συνεχίζει την υπάρχουσα αλυσίδα διανομής. Η είσοδος στην IGONE έγινε με έμμεσο άλμα, οπότε ακολουθεί την ίδια αλυσίδα· η JSR μέσα της είναι η κλήση που επιστρέφει από τον διαχειριστή ML.

Κάθε πέρασμα υπηρεσίας έχει τέλος

Ο διαχειριστής ελέγχει εκκρεμή ερωτήματα, πληκτρολόγιο και συνθήκες παράδοσης, και εξυπηρετεί σειριακά δεδομένα όταν χρειάζεται. Η σειριακή διαδρομή ανταλλάσσει την προσωρινή μνήμη που γέμισε η IRQ με μια μνήμη εργασίας μέσα σε σύντομο τμήμα PHP / SEI / PLP . Αποκωδικοποιεί αυτό το καταγεγραμμένο workLen και επιστρέφει μέσω της γέφυρας. Νέα byte μπορούν να εισέρχονται στην άλλη προσωρινή μνήμη.

04 / ΚΛΕΙΣΤΕ ΤΟΝ ΒΡΟΧΟ ΜΕ ΣΥΜΒΑΝΤΑ

Το φαινομενικό παράδοξο: η WAIT κρατά και την IGONE.

Αν η BASIC περιμένει στη WAIT, πώς φτάνει στην IGONE για να τρέξει η ML; Όσο η συνθήκη δεν ικανοποιείται, δεν μπορεί. Η εκτέλεση μένει στον βρόχο ROM, χωρίς διανομή νέας εντολής BASIC. Η υπηρεσία ML μέσω IGONE παύει μαζί με το περιβάλλον.

Η διέξοδος είναι μια διακοπή υλικού. Η είσοδος IRQ/NMI είναι ανεξάρτητη από τη διανομή εντολών, και η WAIT αφήνει τις διακοπές ενεργές αν ήταν ενεργές στην είσοδο. Άρα ένας χειριστής μπορεί να αλλάξει το byte ειδοποιήσεων ενώ η BASIC μένει στη WAIT.

  1. Η WAIT ελέγχει το κοινόχρηστο byte. Η BASIC μένει στην ίδια εντολή· η υπηρεσία IGONE δεν εκτελείται.
  2. Μια IRQ δημοσιεύει επιλεγμένη ειδοποίηση. Για παράδειγμα, θέτει το bit αφύπνισης επειδή τα σειριακά δεδομένα χρειάζονται επεξεργασία.
  3. Η IRQ επιστρέφει στη διακοπείσα WAIT. Η WAIT ξαναδιαβάζει το byte, βρίσκει τη συνθήκη αληθή και ολοκληρώνει την εντολή.
  4. Η BASIC συνεχίζει τη διανομή εντολών. Ο διερμηνευτής φτάνει στην IGONE, η υπηρεσία ML παίρνει σειρά και ο έλεγχος επιστρέφει στην BASIC.

Στη σειριακή διαδρομή, η SwiftLink NMI λαμβάνει byte. Η IRQ τα μεταφέρει στην προσωρινή μνήμη του πυρήνα και χειρίζεται χρόνο και ποντίκι. Όταν η σειριακή πίεση απαιτεί κύρια επεξεργασία, θέτει το WAKE_UP, bit 0 του κοινού byte ειδοποιήσεων:

irqhandlers.asm · δημοσίευση αφύπνισης κύριας εκτέλεσης
lda notifyFlag
ora #WAKE_UP
sta notifyFlag

Το bit αφύπνισης ικανοποιεί τη WAIT του κύριου βρόχου. Η διανομή συνεχίζεται και δίνει άλλη μία σειρά στην ML. Το περιβάλλον επιβεβαιώνει αυτό το bit και επαναλαμβάνει· δεν χρειάζεται να αποκωδικοποιήσει το μήνυμα μόνο του. Οι εντολές της γραμμής BASIC 113, χωρισμένες και χωρίς το σχόλιο:

aiscbm8.bas · γραμμή BASIC 113
MK=PEEK(MP)
WAIT WF,MK,MS
WM=PEEK(WF) AND MK
IF (WM AND 1) THEN POKE WF,(PEEK(WF) AND 254):GOTO 113
Η σειριακή NMI λαμβάνει byte. Η IRQ θέτει σημαία αφύπνισης. Η WAIT επιστρέφει, η διανομή εκτελεί την υπηρεσία ML της IGONE και αργότερα μια έτοιμη ειδοποίηση σκάφους οδηγεί στον χειριστή BASIC.
Παράδειγμα σειριακής πίεσης: διαβάστε την πάνω σειρά και επιστρέψτε από την κάτω. Η IRQ επιστρέφει στη WAIT· ο έλεγχος σημαίας αφήνει την BASIC να προχωρήσει στην IGONE. Μια παρτίδα μπορεί να χρειάζεται περισσότερα περάσματα πριν παράγει συμβάν σκάφους. Πλήρες μέγεθος ↗

Όταν η ML έχει παραδοτέα ενημέρωση σκάφους, δημοσιεύει το φορτίο και θέτει τη MESSAGE_FLAG (32). Η BASIC τρέχει τον αντίστοιχο χειριστή. Το ρολόι και το ποντίκι δίνουν άλλους λόγους εξόδου από τη WAIT. Το πρώτο μάθημα εξηγεί μάσκες και πρωτόκολλο επιβεβαίωσης.

Το πληκτρολόγιο δείχνει τα δύο στάδια

Η IRQ καταγράφει πλήκτρο και τροποποιητές και θέτει τη WAKE_UP (1). Όταν η BASIC φτάσει στην IGONE, ο διαχειριστής ML επεξεργάζεται το πλήκτρο. Χειρίζεται τις δικές του συντομεύσεις· για πλήκτρο που προωθείται στην BASIC, δημοσιεύει τη KEYPRESS_FLAG (128). Αφύπνιση και ειδοποίηση εφαρμογής είναι ξεχωριστά βήματα σε διαφορετικά περιβάλλοντα εκτέλεσης.

05 / ΤΡΕΙΣ ΡΟΛΟΙ, ΜΙΑ ΕΦΑΡΜΟΓΗ

Ο χρόνος εκτέλεσης και οι ειδοποιήσεις λύνουν διαφορετικά προβλήματα.

Πώς συνδυάζονται οι ευθύνες
ΜέροςΤι του δίνει τον έλεγχο;Ο ρόλος του εδώ
Χειριστές IRQ / NMIΔιακοπές υλικούΛήψη byte, χρονισμός/είσοδος και δημοσίευση αφυπνίσεων.
Διαχειριστής MLΗ παρέμβαση εντολών IGONEΕπεξεργασία δεδομένων προσωρινής μνήμης και ειδοποιήσεις εφαρμογής.
Περιβάλλον BASICΟ βρόχος του διερμηνευτήΑναμονή σχετικών συμβάντων, ενημέρωση περιβάλλοντος και συνέχεια.

Η WAIT χειρίζεται την αναμονή μέσα στη ROM. Η IGONE δίνει ευκαιρίες εκτέλεσης στην ML όσο προχωρά η BASIC. Οι κοινές σημαίες λένε στην BASIC τι έγινε και πότε να συνεχίσει. Έτσι κάθε μέρος διατηρεί τη δική του λογική, χωρίς συνεχή διερμηνεία βρόχου ελέγχου BASIC.

Διαβάστε τον κώδικα

Τα αποσπάσματα προϋποθέτουν αρχικοποιημένο AIS4CBM και την υπάρχουσα διάταξη μνήμης. Οι σύνδεσμοι GitLab δείχνουν στο main· οι αριθμοί γραμμών μπορεί να αλλάξουν.

  • installMainLoop — αποθήκευση, αντικατάσταση και επιδιόρθωση αλυσίδας IGONE.
  • irqbridges.asm — ξεχωριστές γέφυρες IRQ, NMI, USR και IGONE.
  • ais4cbmManager.asm — κύρια εξυπηρέτηση και αποκωδικοποίηση.
  • aiscbm8.bas — βρόχος συμβάντων BASIC και χειριστές.