; ************************************* ; * >EXTENDED VT52-TERMINAL EMULATOR< * ; * Entwickelt mit PROFIMAT-Assembler * ; * M.Schumacher, (p) 12/87 * ; ************************************* LOGBASE equ $44E ; Zeiger auf logischen Bildschirm VID_R0 equ $FFFF8240 ; Video-Farbregister 0 REGISTER equ d3-d7/a0/a3-a6 ;benötigte Register für Verschieberoutinen RESETCO equ INIT_CONOUT ; Termina1-Initialisierungs-Routine NVBLS equ $454 ; Anzahl VBL-Routinen VBLQUEUE equ $456 ; Zeiger auf Zeiger auf VBL-Routinen text INSTALL: move.l 4(a7),a4 ; ^Basepage move.l 12(a4),d7 ; Länge TXT add.l 20(a4),d7 ; +DTA add.l 28(a4),d7 ; +BSS add.l #256,d7 ; +256 move.l d7,-(a7) ; = Anzahl zu reservierender Bytes clr.l -(a7) ; SUPER ON move.w #$20,-(a7) trap #1 move.l d0,2(a7) ; alten SSP merken bsr INIT_CONOUT ; Emulator initialisieren move.l VBLQUEUE,a0 ; AVBL-Zeiger move.w NVBLS,d0 ; Anzahl der Routinen subq.w #1,d0 ; in dbra-Zähler wandeln \test_slot: tst.l (a0) ; Slot frei? beq.s \freeslot ; ja addq.l #4,a0 ; sonst halt dbra d0, \test_slot ; den nächsten Slot testen bra.s \noslot ; kein freier Slot mehr?! (normal unmöglich) \freeslot: lea CRS_IRR,a1 ; Cursor-Interrupt-Routine move.l a1,(a0) ; einbinden \noslot: lea TRAPS,a4 ; ^Speicher für Origina1-Trap-Handler move.l $84, (a4)+ ; alten GEMDOS-Vektor merken lea GEMDOS,a2 ; und neuen move.l a2,$84 ; installieren move.l $88, (a4)+ ; dasselbe mit GEM-Vektor lea AES_VDI,a2 move.l a2,$88 move.l $B4,(a4)+ ; BIOS-Vektor lea BIOS,a2 move.l a2,$B4 move.l $B8,(a4) ; XBIOS-Vektor lea XBIOS,a2 move.l a2,$B8 trap #1 ; SUPER OFF addq.l #6,a7 ; Stack aufräumen move.w #$31,-(a7) ; KEEP PROCESS (hi zombie!) trap #1 ; auf Wiedersehen im GEMDOS ;***************************************** ;* G E M D O S / A E S+V D I / B I O S / * ;* X B I O S - TRAPHANDLER * ;***************************************** SAVE_REGS: ; Register retten move.l (a7)+,d0 ; Rücksprungadresse retten lea SAVE_TOP,a1 ; A1=Registerspeicher, Obergrenze move.w (a7)+,-(a1) ; save (SR) move.l (a7)+,-(a1) ; save(PC) movem.l d3-d7/a3-a7,-(a1) ;save(Register) lea 2(a0),a7 ; SSP auf 1. Parameter setzen move.l d0,-(a7) ; Rücksprungadresse zurück rts REST_REGS: ; Register zurückholen lea SAVE_BOT,a1 ; A1=Registerspeicher, Untergrenze movem.l (a1)+,d3-d7/a3-a7 /Register zurück move.l (a1)+,-(a7) ; PC zurück move.w (a1)+,-(a7) ; SR zurück rte ; zurück ins aufrufende Programm SAVE_BOT: ; Untergrenze Zwischenspeicher ds.w 23,0 ; Platz für Register, PC und SR SAVE_TOP: ; Obergrenze Zwischenspeicher GEMDOS: movea.l a7,a0 ; AO-SSP; btst #5,(a0) ; if (Aufruf aus S-Mode) beq.s \from_user addq.l #6,a0 ; then Offset addieren bra.s \test_pline ; (PC.L+SR.W) \from_user: move.l USP,a0 ; else User Stack benutzen; \test_pline: cmpi.w #9, (a0) ; if (Funktion == PRINT LINE) beq.s \pline ; then eigene Funktion benutzen cmpi.w #64, (a0) ; else if (Funktion == WRITE) beq.s \write ; then auf Ausgabe-Kanal prüfen cmpi.w #2,(a0) ; else if (Funktion == CCONOUT) beq.s \cconout ; then eigene Funktion benutzen cmpi.w #6, (a0) ; else if (Funktion != CRAWIO) bne.s \orig ; then Originalroutine rufen cmpi.b #-1,3 (a0) ; else if (Zeichen — 255) beq.s \orig ; then Original aufrufen \cconout: bsr SAVE_REGS ; Register retten bsr CON_OUT ; Zeichen ausgeben bra REST_REGS ; Register zurück, fertig \pline: bsr SAVE_REGS ; Register retten bsr WRITE ; String ausgeben bra REST_REGS ; Register zurück, fertig \orig: move.l TRAPS,a0 ; else Originalroutine jmp (a0) ; benutzen \write: cmpi.w #1,2(a0) ; Ausgabe auf CON:? bne.s \orig ; nein, ham wa nix mit zu tun addq.w #2,a0 ; sonst Anzahl auszugebender move.l 2(a0),d1 ; Zeichen holen beq.s \retour ; kein Zeichen ist definitiv zu wenig! bsr SAVE_REGS ; ansonsten Register retten addq.w #4,a7 ; (a7) - String-Pointer move.l d1,-(a7) ; Anzahl auf Stack retten move.l 4(a7),-(a7) ; String-Pointer auf Stack legen \lp: move.l (a7),a0 ; String-Pointer holen addq.l #1,(a7) ; auf nächstes Zeichen zeigen lassen move.b (a0),d1 ; Zeichen holen and.w #$FF,d1 ; nur LSB beachten move.w d1,-(a7) ; Zeichen auf Stack legen bsr CON_OUT ; und ausgeben addq.w #2,a7 ; Stack korrigieren subq.l #1,4(a7) ; Anzahl Zeichen dekrementieren bne.s \lp ; und nächstes Zeichen ausgeben addq.l #8,a7 ; Zähler und Pointer löschen bra REST_REGS ; Register restaurieren und zurückspringen \retour: rte AES_VDI: cmpi.w #$73,d0 ; VDI-Aufruf? bne.s \orig ; nein, AES move.l a0,-(a7) ; a0 retten move.l d1,a0 ; ^ParameterBlock move.l (a0),a0 ; ^ContrlBlock cmpi.w #5,(a0) ; VDI-ESC-Sequenz? (CONTRL[0)) bne.s \op_close ; nein lea 10(a0),a0 ; ^Unterfunktionsnummer (CONTRL[5) cmpi.w #16,(a0) ; <16? bmi.s \ok ; ja, eigene Routine ausführen cmpi.w #101,(a0) ; VDI ESC 101? beq.s \ok ; ja cmpi.w #102, (a0) ; Font installieren? bne.s \fail ; nein, dann Originalroutine benutzen \ok: ; *** eigene Routinen ausführen *** lea VDI_TOP,a0 ; ^Zwischenspeicher move.l (a7)+,-(a0) ; alten Inhalt von aO retten move.w (a7)+,-(a0) ; SR move.l (a7)+,-(a0) ; PC movem.l d0-d7/a1-a7,-(a0) /alle Register retten bsr VDI_ENTRY ; Routine ausführen lea VDI_BOT,a0 ; ^Zwischenspeicher movem.l (a0)+,d0-d7/a1-a7 ;Register wieder zurück move.l (a0)+,-(a7) ; pc, move.w (a0)+,-(a7) ; SR und move.l (a0)+,a0 ; a0 restaurieren, rte ; Exception beenden \fail: move.l (a7)+,a0 ; a0 restaurieren \orig: move.l TRAPS+4,-(a7) ; Originaladresse AES/VDI rts ; benutzen \op_close: cmpi.w #100,(a0) ; openVirtualScreenWorkstation? bne.s \close ; nein lea TCB,a0 ; ^TerminalControlBlock clr.l (a0) ; Zeile und Spalte auf 0 bra.s \fail ; zusätzlich Originalroutine ausführen \close: cmpi.w #101,(a0); CloseVirtualScreenWorkstation? bne.s \clear ; nein lea CCB,a0 ; ^CursorControlBlock bclr #3,(a0) ; Cursor abschalten bra.s \fail ; und Originalroutine ausführen \clear: cmpi.w #3,(a0) ; ClearWorkstation? bne.s \fail ; nein movem.i d0-d7/a1-a5,-(a7) ; Register retten lea TCB, a0 ; ^TerminalControlBlock bsr CLS ; Bildschirm löschen movem.i (a7)+,d0-d7/a1-a5 ; Register zurück bra.s \fail ; zusätzlich Origina1routine ausführen VDI_BOT: ds.w 35,0 ; Platz für d0-a7, SR & PC VDI_TOP: BIOS: movea.l a7,a0 ; A0=SSP; btst #5,(a0) ; if (Aufruf aus S-Mode) beq.s \from_user addq.l #6,a0 ; then Offset addieren bra.s \test_wr_asc ; (PC.L+SR.W) \from_user: move.l USP,a0 ; else User Stack benutzen; \test_wr_asc: cmpi.w #12,(a0) ; if (Funktion != WRITE_ASC) bne.s \test_bconout ; then auf BCONOUT testen bsr SAVE_REGS ; else { Register retten; bsr WRITE_ASC ; String ausgeben; bra REST_REGS ; Register zurück; beenden } \test_bconout: cmpi.w #3,(a0) ; if (Funktion == BCONOUT) beq.s \test_dev ; then auf Ausgabegerät testen \orig_bios: move.l TRAPS+8, - (a7) ; else Originalroutine benutzen rts \test_dev: addq.w #2,a0 ; Zeiger auf Gerätenummer setzen cmpi.w #2, (a0) ; if (Ausgabegerät != Console) bne.s \test_dev_vid ; then auf ASCII-Ausgabe testen bsr SAVE_REGS ; else { Register retten; bsr CON_OUT ; Zeichen ausgeben; bra REST_REGS ; Register zurück; beenden ) \test_dev_vid: cmpi.w #5, (a0) ; if (!ASCII_Ausgabe) bne.s \orig_bios ; then Originalroutine benutzen bsr SAVE_REGS ; else { Register retten; bsr ASC_OUT ; Zeichen ausgeben; bra REST_REGS ; Register zurück; beenden ) XBIOS: movea.l a7,a0 ; A0=SSP; btst #5,(a0) ; if (Aufruf aus S-Mode) beq.s \from_user addq.l #6,a0 ; then Offset addieren (PC.L+SR.W) bra.s \test_cursconf \from_user: move.l USP,a0 ; else User Stack benutzen; \test_cursconf: cmpi.w #21,(a0) ; if (Funktion == CURSCONF) beq.s \cursconf ; then eigene Funktion benutzen cmpi.w #100, (a0) ; else if (Funktion INIT_SCREEN) beq.s \init ; then Bildschirmausgabe initialisieren move.l TRAPS+12,-(a7) ; else Originalroutine benutzen rts \cursconf: bsr SAVE_REGS ; Register retten bsr CURSCONF ; eigene Routine ansto_en bra REST_REGS ; Register zurück, beenden \init: bsr SAVE_REGS ; Register retten bsr.s INIT_CONOUT ; Ausgabe initialisieren bra REST_REGS ; Register zurück, beenden ; *********************************************** ; * NEUE ROUTINEN FÜR VT52 UND VDI-ESCAPES * ; *********************************************** INIT CONOUT: ; ESC 'i', Terminal initialisieren dc.w $A000 ; Line A-Pointer holen lea TCB,a2 ; TerminalControlBlock initialisieren move.l 4(a1),a4 ; ^8*8-Font move.l $4C (a4),28(a2) ; ^Fontdaten speichern move.l 8(a1),a4 ; ^8*16-Font move.l $4C(a4),a4 ; ^Fontdaten move.l a4,24 (a2) ; speichern move.l a4,32(a2) ; und 8x16 zum aktuellen Font machen move.l #$4f0018,4(a2) ; max.Spalte=79, max.Zeile=24 move.b #2,8(a2) ; Wrapping ein-, inverse Darstellung ausschalten clr.w 10 (a2) ; keine Cursor-Position gespeichert (ESC 'j') move.l #$100500,36(a2) ; Zeichenhöhe 16 Pixels, 16*80 Bytes/Textzeile lea LINE_A,a2 ; Line-A Adresse move.l a0,(a2) ; merken lea VEC_BASE,a2 ; Ausgabevektor initialisieren lea STD VEC,a3 ; ^Standardausgabe move.l a3,(a2) ; als Default übernehmen lea CCB,a4 ; CursorControlBlock initialisieren move.w #$200, (a4) ; Cursor ausschalten, Blinkmodus wählen move.l #$140014,2 (a4) ; Blinkrate und -Zähler auf 20 stellen move.l LOGBASE,6(a4) ; abs. Cursorposition auf Bildschirm-Anfang lea BEL_ADR,a0 ; ^Speicher für Adresse Gong-Routine move.l #$FC201C,d0 ; TOS-Adresse (Version vom 06.02.1986) cmpi.w #$1986,$FC001A ; "altes" TOS? beq.s \old_tos ; ja move.l #$FC2270,d0 ; sonst Blitter-TOS-Adresse nehmen \old_tos: move.l d0, (a0) ; Vektor installieren lea TCB,a0 ; ^TerminalControlBlock bsr CLS ; Bildschirm löschen+Home bsr DEF_TABS ; Tabulatoren auf default setzen bra UPDATE_END ; Cursor freigeben und zurück CRS_IRR: ; Cursor-Interruptroutine move.l $88,d1 ; derzeit aktive AES/VDI-Adresse lea AES_VDI,a0 ; eigenen Trap-Handler neu installieren move.l a0,$88 ; AES/VDI überlisten (grins) cmp.l a0,d1 ; hat sich Adresse geändert? bne UPDATE_CRS ; dann Cursor abschalten lea CCB,a4 ; ^CursorControlBlock btst #3,(a4) ; Cursor eingeschaltet? beq.s \irr_end ; nein, fertig btst #0,(a4) ; wird gerade Text ausgegeben? beq.s \irr_end ; ja, dann nicht stören btst #1,(a4) ; Blinken eingeschaltet? bne.s \blink ; ja btst #2, (a4) ; Cursorposition schon invertiert? bne.s \irr_end ; ja, nicht mehr invertieren \inv: bsr CUR_INV ; Cursorposition invertieren \irr_end: rts ; zurück zum IR-Slot-Handler \blink: subq.w #1,4(a4) ; Blinkzähler — bne.s \irr_end ; noch nicht 0, fertig move.w 2(a4),4(a4) ; sonst Zähler neu laden bra.s \inv ; invertieren und zurück CURSCONF: ; XBIOS (21) bsr UPDATE_CRS ; Cursor abschalten move.w 4(a7),d1 ; Funktionsnummer holen bne.s \f1 ; nicht 0 bclr #3,(a4) ; Cursor ausschalten bra.s \zurück ; fertig \f1: cmpi.w #1,d1 ; Fkt.-Nr. 1? bne.s \f2 ; nein bset #3,(a4) ; Cursor einschalten bra.s \zurück \f2: cmpi.w #2,d1 ; Fkt.-Nr. 2? bne.s \f3 ; nein bset #1,(a4) ; Cursor in Blinkmodus versetzen bra.s \zurück \f3: cmpi.w #3,d1 ; Fkt.-Nr. 3? bne.s \f4 ; nein bclr #1,(a4) ; Blinkmodus ausschalten bra.s \zurück \f4: cmpi.w #4,d1 ; Fkt.-Nr. 4? bne.s \f5 ; nein move.w 6(a7),2(a4) ; Blinkrate setzen move.w 6(a7),4(a4) ; Zähler initialisieren bra.s \zurück \f5: cmpi.w #5,01 ; Fkt.-Nr. 5? bne.s \zurück ; nein, ignorieren clr.l d0 ; d0.L wegen Wortoperation löschen move.w 2(a4),d0 ; Blinkrate holen \zurück: move.l d0,-(a7) ; Rückgaberegister retten bsr UPDATE_END ; Cursor wieder freigeben move.l (a7)+,d0 ; Register zurück rts WRITE_ASC: ; String ausgeben (ohne Steuerzeichen) move.l 4(a7),a6 ; ^Ausgabestring \lp: move.b (a6)+,d0 ; auszugebendes Byte holen beq.s \ende ; falls 0: fertig bsr.s ASC_ENTRY ; ausgeben bra.s \lp ; bis String-Ende \ende: rts ASC_OUT: ; DIREKTE ZEICHENAUSGABE(OxOO-OxFF) move.w 4(a7),d0 ; Zeichen holen ASC_ENTRY: andi.w #$FF,d0 ; nur Byte beachten bra PUT ; und ausgeben WRITE: ; String ausgeben (mit Steuerzeichen) move.l 4 (a7),a6 ; ^Ausgabestring \lp: move.b (a6)+,d1 ; auszugebendes Byte holen bne.s \aus ; ausgeben, falls<>O lea TCB+9,a0 ; ^Grafik-Flag tst.b (a0) ; Grafik eingeschaltet? beq.s \ende ; nein, fertig \aus: move.b d1,d0 ; Zeichen ausgeben bsr.s CON_ENTRY bra.s \lp ; bis String-Ende \ende: rts CON_OUT: ; AUSGABE MIT STEUERZEICHEN move.w 4(a7),d0 ; Zeichen holen CON_ENTRY: andi.w #$FF,d0 ; LSB isolieren lea VEC_BASE,a1 ; ^Vektor move.l (a1),a0 ; Vektor holen jmp (a0) ; und anspringen STD_VEC: cmpi.w #" ",d0 ; Zeichen < Blank? bpi PUT ; nein, ausgeben cmpi.w #$1B,d0 ; ESC? bne.s \control ; nein lea ESC_SEQ,a0 ; AESC-Handler move.l a0,(a1) ; als Vektor für nächstes Zeichen merken \return: rts ; fertig \control: subq.w #7,d0 ; <7? bmi.s \return ; wenn ja, dann nicht beachten cmpi.w #7,d0 ; >13? bpl.s \return ; ja: Ausgabe unterdrücken lea BEL,a1 ; ^dingelingeling add.w d0,d0 ; Zeichenoffset in Word-Pointer umwandeln move.w CTRL(PC,d0.w),a2 ; entsprechenden Vektor add.l a1,a2 ; +Offset 1. Routine lea TCB,a0 ; ^TerminalControlBlock bsr.s UPDATE_CRS ; Cursor abschalten jsr (a2) ; ausführen bra.s UPDATE_END ; Cursor wieder einschalten CTRL: dc.w 0 ; Glocke dc.w BS-BEL ; Backspace dc.w TAB-BEL ; Tabulator dc.w LF-BEL ; Zeilenvorschub dc.w LF-BEL ; Vertikaltabulator (wie Zeilenvorschub) dc.w LF-BEL ; Formularvorschub (wie Zeilenvorschub) dc.w CR-BEL ; Wagenrücklauf UPDATE_CRS: ; Cursor für Textausgabe ausschalten lea CCB,a4 ; ^CursorControlBlock bclr #0,(a4) ; disable Cursor btst #2,(a4) ; Cursor sichtbar? beq.s \return ; nein bra CUR_INV ; sonst Cursorposition invertieren \return: rts UPDATE_END: ; Cursor wieder freigeben lea TCB,a0 ; ^TerminalControlBlock move.l LOGBASE,a1 ; ^Video-RAM move.w 38(a0),d0 ; Bytes/Textzeile mulu 2(a0),d0 ; * akt. Zeile adda.l d0,a1 ; + Bildschirm-Anfang adda.w (a0),a1 ; + akt. Spalte move.l a1,6(a4) ; = abs. Cursorposition bset #0,(a4) ; enable Cursor rts ESC_SEQ: ; Esc-Sequenz verarbeiten; Einsprung mit: ; d0.w = Steuerzeichen; a1 = AVEC_BASE lea VEC_BASE, a1 ; Ausgabe-Vektor lea STD_VEC,a0 ; wieder auf die normale Ausgabe biegen move.l a0,(a1) ; umschalten subi.w #"A",d0 ; Offset abziehen bmi.s \esc_fail ; zu klein, ungültig cmpi.w #"Z"-"A",d0 ; <="Z"? ble.s \esc_big ; ja, entsprechende Routine ausführen bra.s \esc_sml ; sonst auf Kleinbuchstaben testen \esc_fail: rts \esc_big: bsr.s UPDATE_CRS ; Cursor ausschalten lea CRS_UP,a1 ; Adresse der 1. Routine add.w d0,d0 ; Zeichen als Wort-Offset lea E_BIG,a2 ; Adress-Tabelle move.w 0(a2,d0.w),a2 ; Adress-Offset holen adda.l a1,a2 ; + 1. Routine lea TCB,a0 ; ^TerminalControlBlock jsr (a2) ; Routine ausführen bsr.s UPDATE_END ; Cursor wieder ein rts ; fertig \esc_sml: subi.w #"a"-"A",d0 ; Offset für Kleinbuchstaben abziehen bmi.s \esc_fail ; <"a", ignorieren cmpi.w #"z"-"a",d0 ; >"z"? bgt.s \esc_fail ; ja, ignorieren bsr UPDATE_CRS ; Cursor ausschalten lea SET_COLOR,a1 ; Adresse der 1. Routine add.w d0,d0 ; Zeichen als Wort-Offset lea E_SML,a2 ; AAdress-Tabelle move.w 0(a2,d0.w),a2 ; Adress-Offset holen adda.l a1,a2 ; + 1. Routine lea TCB,a0 ; ^TerminalControlBlock jsr (a2) ; Routine ausführen bsr UPDATE_END ; Cursor einschalten rts ; fertig