home *** CD-ROM | disk | FTP | other *** search
- ; Assemble as COM-File!!!
- ;Dieses Programm hängt sich als Treiber in den INT10 ein und übernimmt Aufrufe
- ;in den VideoModes 7 und 8. Letzterer ist der von diesem Programm neu unter-
- ;stützte Herkules GraphikMode mit 720x360 Punkten. Der Vorteil von diesem Pro-
- ;gramm liegt darin, daß z.B. MSDOS nun auch im Herkules-GraphMode die Zeichen
- ;richtig darstellt. Leider müssen im GraphikMode beim Scrollen 32kB verschoben
- ;werden, was die Sache deutlich bremst. Um das zu umgehen, versucht das Pro-
- ;gramm, solche aufwendigen Scrolls durch Verschieben der DisplayStart-Adresse
- ;zu Beschleunigen. Ein Beispiel: Wenn der ganze Screen um eine Zeile nach oben
- ;geschoben werden soll, so muß man dazu 32670 Bytes bewegen. Setzt man aber
- ;die DisplayStart-Adresse des 6845 um 90 Bytes weiter, so hat das den
- ;gleichen Effekt wie das Scrollen, ist aber bedeutent schneller. Aber:
- ;Wenn man nur einen Teil des Screens verschieben will, so wird beim Umpro-
- ;grammieren des 6845 der Rest mitverschoben. Letzter muß daher vorher in die
- ;umgekehrte Richtung gescrolled werden, nach dem Ändern des DispStart ist
- ;er dann wieder an der richtigen Stelle. Beispiel:
- ; ╔═════╤═════════════════════════════╤═════╗
- ; ║ │ 3 │ ║
- ; ║ ╔═════════════════════════════╗ ║
- ; ║ ║ ║ ║
- ; ║ ║ ║ ║
- ; ║ ║ ║ ║
- ; ║ 2 ║ 1 ║ 4 ║
- ; ║ ║ ║ ║
- ; ║ ║ ║ ║
- ; ║ ║ ║ ║
- ; ║ ╚═════════════════════════════╝ ║
- ; ║ │ 3 │ ║
- ; ╚═════╧═════════════════════════════╧═════╝
- ;Soll Window 1 um eine Zeile nach oben gescrolled werden, so werden statt
- ;dessen nacheinander Bereiche 2, 3 und 4 (gehen über die vertikalen
- ;Bildschirmgrenzen herum!) um eine Zeile nach unten rotiert(!),
- ;und dann der DispStart um eine Zeile nach unten versetzt.
- ;Der Bildschirmspeicher der Herkules-Karte im Graphik-Modus ist in vier
- ;BitMaps unterteilt, die an den Adressen $B0000, $B2000, $B4000, $B6000
- ;beginnen, bzw in der zweiten Page $B8000, $BA000, $BC000 und $BE000.
- ;Aufeinanderfolgende Zeilen entstammen folgenden BitMaps:
- ;0 : BitMap 0 Zeile 0
- ;1 : BitMap 1 Zeile 0
- ;2 : BitMap 2 Zeile 0
- ;3 : BitMap 3 Zeile 0
- ;4 : BitMap 0 Zeile 1
- ;5 : BitMap 1 Zeile 1
- ;und so weiter. Vier aufeinanderfolgende Zeilen aus den BitMaps 0, 1, 2 und 3
- ;nennt man einen "Scan". Ein HiresZeichen aus dem 8x8-Font besteht somit aus
- ;2 Scans. Das Programm 'versteht' im Graphik-Modus nur zwei Attribut-Nibbles,
- ;von denen das High-Nibble für den Hintergrund und das Low-Nibble für das
- ;Zeichen zuständig sind. Sind die 3 LSBs eines Nibbles 0, so wird schwarz,
- ;in allen anderen Fällen weiß geschrieben.
- ;Zur Drucker-Ansteuerung bei Hardcopies müssen eventuell 6 Pascal-Strings
- ;angepasst werden, die sich im erzeugten COM-File nach dem Laden durch
- ;Debug an Offsets $110, $120, $130 etc befinden. Jeder String besteht aus
- ;einem LängenByte und maximal 15 Zeichen. Der erste wird am Beginn, der zweite
- ;am Ende einer TextScreen-HardCopy zum Drucker mit der Nummer, die an Offset
- ;$108 steht. Die nächsten zwei werden am Anfang sowie am Ende einer Graph-
- ;Screen-HardCopy gesendet, die letzten zwei am Anfang bzw am Ende jeder Zeile
- ;der Graphik-Hardcopy. Sie sollten den Drucker darüber informieren, daß jetzt
- ;720 Bytes 8Bit-Graphik kommen (wobei die 8 Bit SENKRECHT stehen!) bzw das
- ;Papier eine Zeile (bei Epson-Druckern also 8/72, nicht 1/6 Zoll) hoch-
- ;schieben.
- ;=============================================================================
- BsLineUp EQU 0 ;Wenn der Cursor am Anfang der Zeile steht und dann ein
- ;CHR(8) kommt, soll der Cursor dann einfach dort stehen
- ;bleiben (0) oder auf das letzte Zeichen der vorherigen
- ;Zeile gesetzt werden (<>0) ?
- OptChar EQU '/'
- False EQU 0
- True EQU NOT False
- Dunnow EQU 01010101b ;(weder False noch True...)
- Ret_Ok EQU 0
- Ret_Expl EQU 1
- Ret_Error EQU 2
- NrOfDotsX EQU 720
- NrOfDotsY EQU 360
- ScrSize EQU NrOfDotsX/8*NrOfDotsY
- NrOfChars EQU NrOfDotsX/8 ;Im GraphikMode wird ein
- NrOfLines EQU NrOfDotsY/8 ;8x8-Zeichensatz benutzt.
- WholeScr EQU NrOfChars*NrOfLines
- BufSize EQU WholeScr*2/2 ;Zum Rotieren wird im GraphikMode ein Buffer
- ;benötigt. Da jede der 4 Pages einzeln rotiert wird, braucht man
- ;pro Zeichen keine 8 sondern nur 2 Bytes. Außerdem wird um maximal
- ;die halbe Bildschirm-Höhe rotiert, sonst würde andersrum rotiert.
- StackSize EQU 2*(24+24) ;24 Worte für mich und 24 für sonstige Irq Routine.
- Page0Seg EQU 0B000h
- Page1Seg EQU 0B800h
- HGC_Diag EQU 00b
- HGC_Text EQU 10b
- HGC_Full EQU 11b
- IndexReg EQU 3B4h
- DataReg EQU IndexReg+1
- ModeReg EQU 3B8h
- StatusReg EQU 3BAh
- ConfigReg EQU 3BFh
- ;=============================================================================
- ;Hinter "IndexReg" und "DataReg" verbergen sich 16 Register des 6845. Um auf
- ;eines zugreifen zu können, muß seine Nummer in das "IndexReg" geschrieben
- ;werden, dann kann das selektierte Register durch das "DataReg" gelesen oder
- ;beschrieben werden.
- ;Regs 0-3 : Timing innerhalb einer Zeile
- ;Regs 4-7 : Timing innerhalb des Bildes
- ;Regs 8-9 : Miscellanous (wie schreibt man das?)
- ;Regs 10-11 : CursorShape
- ;Regs 12-13 : DispStart-Adress (Hi und Lo)
- ;Regs 14-15 : CursorPos-Adress (Hi und Lo)
- ;ModeReg: x x x x x x x x
- ; 2nd Page ┘ │ │ │ │ │ │ └ ?
- ; ? ┘ │ │ │ │ └ Hires
- ; Blink enable ┘ │ │ └ ?
- ; ? ┘ └ Display Enable
- ;StatusReg: x x x x x x x x
- ; Vert retr ┘ │ │ │ │ │ │ └ Horiz retr
- ; ? ┘ │ │ │ │ └ ?
- ; ? ┘ │ │ └ ?
- ; ? ┘ └ Cur Pixel
- ;ConfigReg: x x x x x x x x
- ; ? ─┴─┴─┴─┴──┴─┘ │ └ Enable Graphic Mode
- ; └ Enable 2nd Page
- ;=============================================================================
-
- BiosData SEGMENT AT 40h
- ORG 49h
- CurVideoMode DB ?
- NrOfColumns DW ?
- BytesPerPage DW ?
- CurDispStart DW ?
- CursPosTable DW 8 DUP (?)
- CurCursShape DW ?
- ActivePage DB ?
- VidCtrlIoAdr DW ?
- ModeRegCont DB ?
- ORG 78h
- TimeOutTable DB 4 DUP (?)
- ORG 84h
- LastTextLine DB ?
- BiosData ENDS
-
- TheProgram SEGMENT BYTE PUBLIC
- ASSUME CS:TheProgram,DS:BiosData,ES:NOTHING,SS:NOTHING
- ORG 100h
-
- EntryPoint PROC NEAR
- jmp Main
- DB 13,'(c)Stressi',26
- EntryPoint ENDP
-
- ORG 10Fh
- PrnInfo LABEL BYTE ;Ab hier stehen die Drucker-Daten für "PrtScr":
- PrinterNr DB 0 ;Zuerst die Nummer des zu benutzenden Druckers,
- PrnTextInit DB 2,13,10 ;dann die Strings, die vor bzw nach
- DB 16-($-PrnTextInit) DUP(-1)
- PrnTextExit DB 0 ;einer Text-HardCopy gesendet werden,
- DB 16-($-PrnTextExit) DUP(-1)
- PrnGraphInit DB 2,13,10 ;die Strings, die vor bzw nach
- DB 16-($-PrnGraphInit) DUP(-1)
- PrnGraphExit DB 2,10,10 ;einer Graph-HardCopy gesendet werden
- DB 16-($-PrnGraphExit) DUP(-1)
- PrnLineStart DB 4,27,'L',LOW NrOfDotsX,HIGH NrOfDotsX ;sowie die, die vor
- DB 16-($-PrnLineStart) DUP(-1)
- PrnLineEnd DB 4,13,27,'J',24 ;bzw nach jeder Graph-Zeile gesendet werden.
- DB 16-($-PrnLineEnd) DUP(-1)
- PrnInfoSize EQU $-OFFSET PrinterNr
-
- ReadLightpen EQU NotImplemented
- ColorPalette EQU NotImplemented
- Reserved EQU NotImplemented
- FuncTable DW SetVideoMode,SetCursorShape,SetCursorPos,ReadCursorPos
- DW ReadLightpen,SelActivePage,ScrollUp,ScrollDown
- DW ReadAttrChar,WriteAttrChar,WriteCharOnly,ColorPalette
- DW WriteDot,ReadDot,TeletypeOut,GetVideoState,Reserved
- DW Reserved,Reserved,WriteString
- GraphModeTiming DB 54,45,47, 7,95,0,90,90,2, 3, 0, 0,0,0 ;14 Bytes...
- TextModeTiming DB 97,80,82,15,25,6,25,25,2,13,11,12,0,0
- DispStartTable DW ?,? ;ByteOfs für je eine Page,
- PageSegment DW Page0Seg,Page1Seg ;bezogen auf diese Segmente.
- FastScrollFlag DB Dunnow
- PrtScrActive DB False
- Int10Active DB False
- SavedInt10Vec DW ?,?
- SavedInt5Vec DW ?,?
- SavedInt10Stack DW ?,?
- SavedInt5Stack DW ?,?
- PrtScr8x8Buffer DB 8,8 DUP (?) ;zur Konvertierung: In der BitMap liegen
- ;die Bits waagerecht, der Drucker braucht sie senkrecht.
-
- GetPageArrayIndex MACRO ArrayBase,DestReg ;ZF=PageNr.
- LOCAL IsPage0
- mov DestReg,ArrayBase
- jz IsPage0
- inc DestReg
- inc DestReg
- IsPage0:
- ENDM
-
- DisplayAdress_Text MACRO XPos,YPos
- ;XPos und YPos dürfen weder AL noch AH sein !!!
- mov al,80
- mul YPos
- add al,XPos
- adc ah,0
- ENDM ;AX = WordAdress im VideoRam.
-
- DisplayAdress_Graph MACRO XPos,YPos ;XPos in Chars, YPos in Scans.
- ;XPos und YPos dürfen weder AL noch AH sein !!!
- mov al,90 ;Eine ScanLine ist 90 Bytes lang,
- mul YPos ;ein Char ist 2 Scans hoch.
- add al,XPos
- adc ah,0
- ENDM ;AX = ByteAdress im VideoRam.
-
- ZeichenSatz8x8 LABEL BYTE
- INCLUDE Herkules.INC
-
- NewInt10Handler PROC FAR
- jmp SHORT HandleInterrupt
- DB 'Herkules V1.0 ' ;müssen 16 Zeichen sein (Konvention...)
- HandleInterrupt:
- cmp cs:Int10Active,True ;"Herkules" schon mal aufgerufen?
- je NotAvailable ;Wir haben aber nur EINEN Stack!
- cmp ah,19 ;FunctionNr>19?
- ja NotAvailable ;Wird von uns nicht unterstützt.
- push si
- mov cs:Int10Active,True ;Flag setzen, daß jetzt auf den
- mov cs:SavedInt10Stack,sp ;internen Stack umgeschaltet wird:
- mov cs:SavedInt10Stack+2,ss
- mov si,cs ;Alten StackPointer merken
- mov ss,si ;und auf neuen Stack umschalten
- mov sp,OFFSET MyInt10Stack+StackSize ;(SP auf das ENDE des Stacks!).
- push ds
- push ax
- mov si,BiosData ;DS auf das Bios-Segment setzen.
- mov ds,si
- xchg al,ah
- mov si,ax ;FunctionNr von AH
- xchg al,ah ;nach SI und von
- and si,00FFh ;Byte nach Word wandeln.
- jz ThatConcernsMe ;Function 0? => In Ordnung.
- mov ah,CurVideoMode
- cmp ah,7 ;Mode 7 (HercText)
- je ThatConcernsMe
- cmp ah,8 ;oder 8 (HercGraf)?
- jne CallOldBios
- ThatConcernsMe: ;Dann geht das uns etwas an:
- sti ;Interrupts enablen,
- shl si,1
- call cs:[FuncTable+si] ;Behandlungs-Routine aufrufen
- pop ax ;mit AH=CurVideoMode.
- pop ds
- mov ss,cs:SavedInt10Stack+2
- mov sp,cs:SavedInt10Stack ;Auf alten Stack zurückschalten und
- mov cs:Int10Active,False ;entsprechendes Flag wieder löschen,
- pop si ;Register restaurieren
- iret ;und zurück.
- NotAvailable:
- jmp dword ptr cs:[SavedInt10Vec]
- CallOldBios:
- call NotImplemented ;Subroutine kommt NICHT zurück...
- NewInt10Handler ENDP
-
- SelActivePage PROC NEAR ;AL=new PageNr
- and al,1 ;Es gibt nur zwei Pages...
- mov ActivePage,al ;PageNr im BiosSeg speichern.
- GetPageArrayIndex 0,si
- push dx ;DX darf nicht verändert werden!
- mov dx,ModeReg
- mov al,ModeRegCont ;Inhalt des ModeReg aus BiosSeg lesen
- and al,7Fh ;Bit für "2nd Page" löschen.
- test ActivePage,1 ;Ist es denn wirklich die erste Page?
- jz SetModeReg ;Dann neuen Wert schreiben.
- or al,80h ;Sonst Bit für "2nd Page" vorher setzen.
- SetModeReg:
- mov ModeRegCont,al ;Neuen Wert a) ins BiosSeg schreiben
- out dx,al ;und b) an den VideoChip schicken.
- pop dx
- mov ax,[CursPosTable+si]
- call UpdateCursorPos ;CursorPos der neuen Page an VideoChip schicken,
- mov ax,cs:[DispStartTable+si]
- jmp UpdateDispStart ;ebenso ihren DispStart.
- SelActivePage ENDP ;AX and SI destroyed.
-
- SetCursorShape PROC NEAR ;CH=CursorStart, CL=CursorEnd
- push dx
- mov CurCursShape,cx ;Neue "CursorShape" im BiosSeg eintragen.
- mov dx,IndexReg
- mov al,11
- out dx,al ;RegisterNr ins IndexReg
- inc dx
- mov al,cl
- out dx,al ;LowByte ins DataReg.
- dec dx
- mov al,10 ;HighByte: Zuerst RegNr
- out dx,al ;ins IndexReg
- inc dx
- mov al,ch ;und dann das DatenByte
- out dx,al ;ins DataReg.
- pop dx
- ret
- SetCursorShape ENDP ;AX destroyed.
-
- SetCursorPos PROC NEAR ;BH=PageNr, (DL/DH)=new CursorPos, AH=CurVideoMode
- mov al,bh
- and al,1 ;Mehr als 2 Seiten gibt's sowieso nicht.
- GetPageArrayIndex OFFSET(CursPosTable),si
- mov [si],dx ;Neue Cursor Position im BiosSeg eintragen.
- cmp al,ActivePage ;Wurde die CursorPos der active Page geändert
- jne SetCursPosDone
- cmp ah,7 ;und ist auch noch der Text-Mode aktiv?
- jne SetCursPosDone
- mov ax,dx ;Dann muß die neue Position
- call UpdateCursorPos ;auch dem 6845 mitgeteilt werden.
- SetCursPosDone:
- ret
- SetCursorPos ENDP ;AX and SI destroyed.
-
- ReadCursorPos PROC NEAR ;BH=PageNr
- test bh,1
- GetPageArrayIndex OFFSET(CursPosTable),si
- mov dx,[si]
- mov cx,CurCursShape
- ret
- ReadCursorPos ENDP ;SI destroyed, CX=CursorShape, DX=CursorPos.
-
- ScrollUp PROC NEAR
- ;AL=NrOfLines, (CH/CL)-(DH/DL), BH=NewAttribute, AH=CurVideoMode.
- cmp ch,LastTextLine
- ja InvalidWindow
- cmp dh,ch ;LowerCorner<=UpperCorner?
- jb InvalidWindow ;Da tun wir am besten gar nix,
- cmp dl,cl ;ebenso bei RightCorner<=LeftCorner.
- jb InvalidWindow
- push bx
- mov bl,bh
- mov bh,ActivePage
- call ScrollPartOfScreenUp
- pop bx
- InvalidWindow:
- ret
- ScrollUp ENDP ;AX and SI destroyed.
-
- ScrollDown PROC NEAR
- ;AL=NrOfLines, (CH/CL)-(DH/DL), BH=NewAttribute, AH=CurVideoMode.
- cmp ch,LastTextLine
- ja InvalidWindow
- cmp dh,ch ;LowerCorner<=UpperCorner?
- jb InvalidWindow ;Da tun wir am besten gar nix,
- cmp dl,cl ;ebenso bei RightCorner<=LeftCorner.
- jb InvalidWindow
- push bx
- mov bl,bh
- mov bh,ActivePage
- call ScrollPartOfScreenDown
- pop bx
- ret
- ScrollDown ENDP
-
- ReadAttrChar PROC NEAR ;BH=PageNr, AH=CurVideoMode
- push dx ;Register retten.
- push di
- push es
- test bh,1
- GetPageArrayIndex 0,di ;Welche von beiden Pages meint der?
- mov dx,[CursPosTable+di] ;Entsprechende CursorPos
- mov es,cs:[PageSegment+di] ;und PageSegment holen.
- cld
- cmp ah,8
- je ReadGraphChar ;GraphMode? Dann andere Routine benutzen.
- DisplayAdress_Text dl,dh ;CursorPos in
- shl ax,1 ;Adresse umrechnen
- mov di,ax
- mov ax,es:[di] ; => dort steht das Attr und der Char.
- jmp SHORT ReadCharDone ;Das war's eigentlich schon,
- ReadGraphChar: ;im GraphMode ist das viel komplizierter:
- push cx
- shl dh,1 ;Cursor YPos von "CharLines" nach "Scans" wandeln
- DisplayAdress_Graph dl,dh ;und in eine Adresse umrechnen, die dann,
- mov di,cs:[DispStartTable+di] ;zum DispStart addiert und
- add di,ax ;auf eine BitMap begrenzt,
- and di,1FFFh ;auf das Graphik-Pattern zeigt.
- mov ah,0FFh ;XOR-Flag auf "revers" setzen.
- SearchAgain:
- mov al,es:[di] ;Das erste Byte des Graphik-Patterns holen
- xor al,ah ;und mit dem XOR-Flag verknüpfen.
- mov si,OFFSET ZeichenSatz8x8
- mov cx,256
- sub si,8 ;Dieses erste Byte nacheinander mit dem
- CompFirstByte: ;jeweils ersten Byte der 256 Zeichen
- add si,8 ;(komplette ASCII-Tabelle)
- cmp al,cs:[si] ;des ZeichenSatzes vergleichen.
- loopne CompFirstByte
- jne UnknownChar
- push si ;Weitertesten und die gesamten
- push di ;8 Bytes Graphik-Pattern
- mov dl,2 ;(gleich 2 Scans) vergleichen.
- CompRestOfChar:
- lods byte ptr cs:[si] ;Nächstes Byte aus dem ZeichenSatz holen,
- xor al,ah ;über das XOR-Flag verknüpfen
- cmp al,es:[di] ;und mit dem nächsten Graphik-Byte vergleichen.
- jne CharCompared
- add di,2000h ;Gleich? Dann eine Zeile tiefer (= nächste BitMap)
- jno CompRestOfChar ;es sei denn, das war schon die letzte.
- add di,90 ;In diesem Fall Zeiger einen Scan tiefer setzen.
- and di,1FFFh ;Dabei PageWrap beachten.
- dec dl ;War das der letze Scan?
- jnz CompRestOfChar ;Nicht? Dann weitervergleichen.
- CharCompared:
- pop di
- pop si ;Vergleich beendet.
- je GraphCharRecognized ;Wie sah denn das Ergebnis aus, gleich
- mov al,es:[di] ;oder ungleich?
- xor al,ah
- test cx,cx ;Ungleich? Dann wieder nach erstem Byte suchen, aber
- jnz CompFirstByte ;nur wenn Zeichensatz noch nicht zuende durchsucht.
- UnknownChar:
- inc ah ;Zeichen nicht erkannt? Dann nochmal mit neuem XOR-Flag pro-
- jz SearchAgain ;bieren, wenn es nicht schon alle Werte angenommen hatte.
- GraphCharRecognized:
- mov al,255
- sub al,cl
- pop cx
- mov ah,07h
- test dl,dl ;Zeichen gefunden. Wie stand denn das XOR-Flag?
- jz ReadCharDone ;Auf reverse?
- mov ah,70h ;Dann entsprechendes Attribute zurückgeben.
- ReadCharDone: ;AL = Char, AH = Attribute.
- pop es
- pop di ;Register zurückholen
- pop dx
- pop si ;und ReturnAdresse kurz lupfen.
- add sp,2 ;Dann kann ich nämlich den gepushten AX vom Stack entfernen
- push ax ;und statt dessen den neuen dorthin legen.
- push si ;Jetzt nur noch die alte ReturnAdresse
- ret ;wieder zurück und wir sind fertig.
- ReadAttrChar ENDP ;AX and SI destroyed.
-
- WriteCharOnly PROC NEAR
- ;BH=PageNr, CX=NrOfChars, AL=CharToWrite, AH=CurVideoMode
- cmp ah,8 ;Ist Graphik-Mode aktiv? Im GraphMode ist es nicht
- mov bl,7 ;möglich, NUR ein Zeichen OHNE Attribute zu schreiben.
- je WriteAttrChar ;Statt dessen "WriteAttrChar" mit Attr=7 benutzen.
- push cx ;Also TextModus. Dann Register retten.
- push dx
- push di
- push es
- jcxz WriteCharDone ;Gar nichts zum Schreiben da? Dann Abbruch.
- cld
- push ax ;CharToWrite zwischenspeichern.
- test bh,1 ;Welche Page denn?
- GetPageArrayIndex 0,di ;Ach so, DIE...
- mov dx,[CursPosTable+di]
- mov es,cs:[PageSegment+di]
- DisplayAdress_Text dl,dh ;CursorPos in Adresse umrechnen,
- shl ax,1 ;nach "ByteOffset" wandeln
- mov di,ax ;ergibt ES:DI = CharAdress.
- pop ax
- WriteCharOnlyLoop: ;Zeichen ins VideoRam donnern:
- stosb ;Character-Byte schreiben
- inc di ;aber Attribute-Byte überspringen.
- loop WriteCharOnlyLoop ;Noch Zeichen da?
- WriteCharDone:
- pop es
- pop di
- pop dx
- pop cx
- ret
- WriteCharOnly ENDP ;AX and SI destroyed.
-
- WriteAttrChar PROC NEAR
- ;BH=PageNr, CX=NrOfChars, AL=Char, BL=Attribute, AH=CurVideoMode
- push cx
- push dx ;Register retten
- push di
- push es
- jcxz WriteCharDone ;Ist überhaupt etwas da zum Schreiben?
- cld
- test bh,1
- GetPageArrayIndex 0,di ;Um welche Page handelt es sich?
- mov dx,[CursPosTable+di] ;Entsprechende "CursorPos"
- mov es,cs:[PageSegment+di] ;und "PageSegment" holen.
- cmp ah,8 ;Sind wir im Graphik-Modus?
- je WriteGraphChar ;Dann andere Routine benutzen.
- push ax ;CharToWrite zwischenspeichern.
- DisplayAdress_Text dl,dh ;CursorPos in Adresse umrechnen,
- shl ax,1 ;nach "ByteOffset" wandeln
- mov di,ax ;ergibt ES:DI = CharAdress.
- pop ax
- mov ah,bl
- rep stosw ;Zeichen ins VideoRam donnern
- jmp SHORT WriteCharDone ;und fertig.
- WriteGraphChar:
- push bx ;Im Graphik-Mode ist alles viel komplizierter:
- push ax ;CharToWrite zwischenspeichern.
- shl dh,1 ;DH von "CharLines" nach "Scans" wandeln
- DisplayAdress_Graph dl,dh ;und daraus die Adresse berechnen,
- mov di,cs:[DispStartTable+di] ;außerdem den DispStart
- add di,ax ;nicht vergessen
- and di,1FFFh ;und begrenzen.
- mov dh,0 ;AND-Flag und
- mov bh,0 ;XOR-Flag löschen.
- test bl,70h ;Soll das Zeichen revers erscheinen?
- jz NotReverse
- dec bh ;Dann XOR-Flag setzen. Soll das Zeichen
- test bl,07h ;revers & white (=> weiß auf weiß) erscheinen?
- jnz GotFlags ;Dann stimmt das mit dem gelöschte AND-Flag.
- SetAndFlag:
- dec dh ;Sonst AND-Flag setzen,
- jmp SHORT GotFlags ;dann stimmen die Flags.
- NotReverse:
- test bl,07h ;Soll das Zeichen black & non revers (=> schwarz auf
- jnz SetAndFlag ;schwarz) erscheinen? Dann auch erst AND-Flag setzen.
- GotFlags:
- mov si,OFFSET ZeichenSatz8x8
- pop ax
- mov ah,0 ;ASCII-Code des Zeichens auf 16Bit erweitern
- shl ax,1 ;und 3x nach links schieben
- shl ax,1 ;(entspricht *8)
- shl ax,1 ;ergibt den Offset
- add si,ax ;in die ZeichenSatz-Tabelle.
- mov bl,2 ;2 komplette Scans (gleich 8 Zeilen)
- mov dl,cl ;zu je CL Zeichen (gleich Bytes) schreiben.
- mov ax,es
- WriteNextLineOfChars:
- mov es,ax
- ComputeWrapPos di
- push di ;ZeilenAnfang für später merken.
- lods byte ptr cs:[si] ;So: Eine Zeile aus dem ZeichenSatz holen,
- and al,dh ;mit den Flags verknüpfen
- xor al,bh ;und soweit schreiben,
- rep stosb ;wie ohne 8k-Wrap möglich ist.
- xor di,di ;Dann Zeiger auf den PageAnfang zurücksetzen
- mov cl,ah ;und auch den Rest schreiben.
- rep stosb
- pop di
- mov ax,es ;Jetzt ES auf nächste BitMap setzen
- add ah,02h ;(=eine Bildschirmzeile tiefer).
- cmp ah,HIGH(Page0Seg)+8
- je PrepareNextScan
- cmp ah,HIGH(Page1Seg)+8
- jb WriteNextLineOfChars ;War das etwa die letzte BitMap?
- PrepareNextScan:
- sub ah,08h ;Dann Zeiger wieder auf die erste BitMap setzen
- add di,90 ;aber einen Scan tiefer.
- and di,1FFFh
- dec bl ;War das der letzte?
- jnz WriteNextLineOfChars ;Nicht? Dann weitermachen.
- pop bx
- pop es
- pop di
- pop dx
- pop cx
- ret
- WriteAttrChar ENDP ;AX and SI destroyed.
-
- WriteDot PROC NEAR ;CX=XPos, DX=YPos, AL=ColorNr, AH=CurVideoMode
- push bx
- push cx ;Register retten.
- push dx
- push es
- cmp ah,8 ;Sind wir auch wirklich im Graphik-Modus?
- jne WriteDotDone ;Im TextModus gibt es keine Punkte zum Setzen.
- push ax ;ColorByte merken.
- test ActivePage,1
- call ComputeDotAdress ;Adresse des Punktes
- mov al,80h ;und seine Maske
- shr al,cl ;berechnen.
- mov ah,al
- xor ah,0FFh ;Byte invertieren als Lösch-Maske.
- pop dx ;ColorByte zurückholen
- test dl,7Fh ;und testen:
- jnz MakeDotWhite ;Soll der Punkt schwarz oder
- mov al,0 ;weiß werden?
- MakeDotWhite:
- test dl,dl ;Soll der Punkt entsprechend gesetzt
- js InvertDot ;oder invertiert werden?
- and es:[bx],ah ;Punkt erst mal löschen (schwarz setzen)
- or es:[bx],al ;und dann bei Bedarf wieder setzen,
- jmp SHORT WriteDotDone ;das war's.
- InvertDot: ;Punkt invertieren?
- xor es:[bx],al ;Dann Byte mit der Maske verknüpfen.
- WriteDotDone:
- pop es
- pop dx
- pop cx
- pop bx
- ret
- WriteDot ENDP ;AX destroyed.
-
- ReadDot PROC NEAR ;CX=XPos, DX=YPos.
- push bx
- push cx
- push dx
- push es
- test ActivePage,1 ;Welche Page ist gerade aktiv?
- call ComputeDotAdress
- mov al,80h ;Maske aus der Nummer
- shr al,cl ;des Bits bestimmen.
- test es:[bx],al ;Ist das Bit gelöscht?
- mov al,0 ;Dann "black" zurückmelden,
- jz DotIsBlack
- mov al,7 ;sonst "white".
- DotIsBlack:
- pop es
- pop dx
- pop cx ;Register wieder holen
- pop bx
- pop si ;und ReturnAdresse kurz lupfen.
- add sp,2 ;Dann kann ich nämlich den gepushten AX vom Stack entfernen
- push ax ;und statt dessen den neuen dorthin legen.
- push si ;Jetzt nur noch die alte ReturnAdresse
- ret ;wieder zurück und wir sind fertig.
- ReadDot ENDP ;AX and SI destroyed.
-
- TeletypeOut PROC NEAR
- ;AL=CharToWrite, BL=ForegroundColor in GraphMode, AH=CurVideoMode
- push bx
- mov bh,ActivePage
- cmp ah,8 ;In welchem VideoMode sind wir gerade?
- je GraphTeletype
- mov bl,al ;TextMode? Dann zu schreibendes Zeichen merken,
- call ReadAttrChar ;Zeichen+Attribute an CursorPos holen
- mov al,bl ;und das neue Zeichen mit dem
- mov bl,ah ;Attribute des alten schreiben.
- GraphTeletype:
- test bh,1 ;In welcher Page sind wir gerade?
- GetPageArrayIndex OFFSET(CursPosTable),si
- call WriteOneChar ;So, jetzt das Zeichen MITSAMT Attribute schreiben
- call UpdateCursorPos ;und neue CursorPos an den 6845 schicken.
- pop bx
- ret
- TeletypeOut ENDP ;AX and SI destroyed.
-
- GetVideoState PROC NEAR
- mov bh,ActivePage
- pop ax ;ReturnAdresse kurz lupfen. Dann kann ich
- add sp,2 ;nämlich den gepushten AX vom Stack entfernen
- push word ptr CurVideoMode ;und statt dessen den neuen Wert dorthin legen.
- push ax ;Jetzt nur noch die alte ReturnAdresse
- ret ;wieder zurück und wir sind fertig.
- GetVideoState ENDP ;AX destroyed, BH=ActivePage.
-
- WriteString PROC NEAR
- ;[ES:BP]=String, CX=NrOfChars, DX=CursStartPos, BH=PageNr, AL=SubModeNr
- ;AH=CurVideoMode, BL=Attribute in some SubModes.
- push bp
- test al,1 ;Wie sieht das LSB der SubMode-Nr aus?
- pushf ;Zustand für später merken!
- test bh,1 ;In welcher Page schreiben?
- GetPageArrayIndex OFFSET(CursPosTable),si
- push [si] ;Entsprechende CursorPos merken
- mov [si],dx ;und StartPos des Strings als CursPos speichern.
- jcxz EndOfString
- and al,2 ;SubMode 0 oder 1? Dann enthält BL
- jz CommonAttribute ;das Attribute für den gesamten String.
- push bx
- IndividualAttributes: ;Sonst besteht der String aus Zeichen UND Attibutes:
- mov ax,es:[bp] ;Davon die erste Char-Attr-Kombination lesen
- inc bp ;und Zeiger vorerst ein Byte weitersetzen.
- cmp al,7
- je NotPrintable
- cmp al,8 ;Jetzt mal sehen:
- je NotPrintable
- cmp al,10 ;Ist das Zeichen weder BEL
- je NotPrintable
- cmp al,13 ;noch BS noch CR noch LF?
- je NotPrintable ;Dann ist das ein printable Char mit einem Attrib-Byte
- inc bp ;dahinter, der Zeiger muß entsprechend korrigiert werden.
- NotPrintable:
- mov bl,ah
- call WriteOneChar ;mit Attr in BL schreiben
- loop IndividualAttributes ;und weiter.
- pop bx
- jmp SHORT EndOfString
- CommonAttribute:
- mov al,es:[bp] ;Erstes Zeichen des Strings lesen
- call WriteOneChar ;und mit BL=Attr schreiben.
- inc bp ;Zeiger weiter und
- loop CommonAttribute ;nochmal von Vorne.
- EndOfString:
- pop ax ;Alte CursorPos VOR dem Schreiben zurückholen,
- popf ;dann Flags testen:
- pop bp
- jz DontMoveCursor ;Sollte der Cursor bewegt werden oder nicht?
- mov ax,[si] ;Ja? Dann neue Position an 6845 schicken.
- DontMoveCursor:
- mov [si],ax ;Sonst alte Position wieder speichern
- jmp UpdateCursorPos ;und an 6845 schicken.
- WriteString ENDP ;AX and SI destroyed.
-
- NotImplemented PROC NEAR
- pop ax ;RückkehrAdresse nach "NewInt10Handler" verwerfen,
- pop ax
- pop ds ;gerettete Register wiederholen,
- mov ss,cs:SavedInt10Stack+2
- mov sp,cs:SavedInt10Stack ;auf alten Stack zurückschalten und
- mov cs:Int10Active,False ;entsprechendes Flag wieder löschen,
- pop si ;dann in den alten
- jmp dword ptr cs:[SavedInt10Vec] ;Int10-Handler springen.
- NotImplemented ENDP
-
- SetVideoMode PROC NEAR ;AL=ModeNr
- cli ;Interrupts erst mal wieder verhindern!
- cmp al,7 ;Soll Modus 7 gesetzt werden?
- je HerkGrafAus ;Machen wir doch.
- cmp al,8
- jne NotImplemented ;Weder 7 noch 8? Ha'm wa' nich.
- push dx ;Also Modus 8 setzen?
- mov CurVideoMode,al ;"VideoMode" im BiosSeg speichern.
- mov dx,8000h
- mov al,HGC_Full
- mov ah,00101011b ;1st Page, Blinker enable, Display enable, Hires
- mov si,OFFSET GraphModeTiming
- jmp SHORT UpdateVideoMode
- HerkGrafAus:
- push dx ;Also Modus 7 setzen?
- mov CurVideoMode,al ;"VideoMode" im BiosSeg speichern.
- mov dx,1000h
- mov al,HGC_Text
- mov ah,00101001b ;1st Page, Blinker enable, Display enable, Text
- mov si,OFFSET TextModeTiming
- UpdateVideoMode:
- ;[CS:SI]=Timing-Daten, AH=ModeReg-Inhalt, AL=Configuration, DX=BytesPerPage
- mov BytesPerPage,dx
- mov dx,ConfigReg
- out dx,al ;Configuration setzen.
- mov dx,StatusReg
- NotYetSync:
- in al,dx
- test al,80h
- jnz NotYetSync ;Auf Vertical Retrace warten.
- mov dx,ModeReg
- mov al,ah
- out dx,al ;Video-Modus umschalten,
- mov ModeRegCont,al ;Inhalt des ModeReg im BiosSegment merken
- mov ah,0
- mov dx,IndexReg ;und die Timing-Daten in einer
- OutStringByteLoop: ;Schleife in den Chip schieben.
- mov al,ah
- out dx,al ;Zuerst die Nummer des Registers
- inc dx ;ins IndexReg,
- lods byte ptr cs:[si] ;dann das nächste Timing-Byte holen
- out dx,al ;und ins DataReg schreiben.
- dec dx
- inc ah
- cmp ah,14
- jne OutStringByteLoop ;Noch weitere Timing-Daten?
- xor ax,ax
- mov CursPosTable+0,ax ;Cursor nach (0/0)
- mov CursPosTable+2,ax ;in Page 0 und 1,
- mov cs:DispStartTable+0,ax ;außerdem
- mov cs:DispStartTable+2,ax ;"DispStart" zurücksetzen.
- call SelActivePage ;Page 0 selektieren und vor dem langen
- sti ;Bildschirm-Löschen Interrupts enablen.
- mov dx,256*25+80 ;Bildschirm enthält 80x25,
- cmp CurVideoMode,8
- jne SmallScreen
- mov dx,256*NrOfLines+NrOfChars ;im Graphik-Modus 90x45 Zeichen.
- SmallScreen:
- mov byte ptr NrOfColumns+1,0
- mov byte ptr NrOfColumns,dl ;Noch ein paar Variablen
- dec dh
- mov LastTextLine,dh ;im Bios-Segment setzen.
- inc dh
- push bx
- push cx
- mov bh,0 ;Bildschirm in Page 0
- mov bl,7 ;mit Attribute 7
- mov cx,0
- call ClearWindow ;löschen,
- inc bh ;ebenso in Page 1.
- mov cx,0
- mov dl,byte ptr NrOfColumns
- mov dh,LastTextLine
- inc dh
- call ClearWindow
- mov cx,0C0Dh
- call SetCursorShape
- pop cx
- pop bx
- pop dx
- ret
- SetVideoMode ENDP ;AX and SI destroyed.
-
- NewInt5Handler PROC FAR
- push ax ;Register retten.
- push ds
- cmp cs:PrtScrActive,True ;Wird gerade schon eine HardCopy gemacht?
- je PrtScrAborted ;Dann diese abbrechen,
- mov ax,BiosData
- mov ds,ax ;sonst DS auf das BiosSegment
- mov ah,CurVideoMode ;setzen und von dort VideoModus lesen.
- cmp ah,7
- je SupportedMode ;Sind wir im Herc-TextMode
- cmp ah,8 ;oder im Herc-GraphMode?
- je SupportedMode ;Dann geht das uns an,
- pop ds ;im anderen Fall Register
- pop ax ;wieder herstellen und an
- jmp dword ptr cs:[SavedInt5Vec] ;alten PrtScr-Handler übergeben.
- SupportedMode:
- mov cs:PrtScrActive,True ;Jetzt geht's gleich los.
- cmp ah,7 ;In welchem VideoModus sind wir?
- mov cs:SavedInt5Stack,sp
- mov cs:SavedInt5Stack+2,ss
- mov ax,cs ;Entsprechendes Flag schon mal setzen
- mov ss,ax ;und auf eigenen Stack umschalten
- mov sp,OFFSET MyInt5Stack+StackSize ;(SP auf das ENDE des Bereichs!).
- mov al,ActivePage
- sti ;Dann kann ich jetzt auch die Interrupts enablen.
- je MakeTextHardcopy
- call PrintGraphScreen ;TextModus? Dann Drucken.
- jmp SHORT PrtScrDone
- MakeTextHardcopy:
- call PrintTextScreen ;GraphModus? Dann andere Druck-Routine benutzen.
- PrtScrDone:
- mov ss,cs:SavedInt5Stack+2
- mov sp,cs:SavedInt5Stack ;Auf alten Stack zurückschalten
- PrtScrAborted:
- mov cs:PrtScrActive,False
- pop ds
- pop ax ;und Register wiederholen.
- iret
- NewInt5Handler ENDP
-
- IF Debugging
- ;----------------------------------------------------------------------------+
- BreakPoint PROC NEAR ;!
- pushf ;!
- push ax ;!
- push dx ;!
- push si ;!
- mov dx,ModeReg ;!
- mov al,00101001b ;!
- out dx,al ;Video-Modus umschalten, ;!
- mov si,OFFSET TextModeTiming ;!
- mov ah,0 ;!
- mov dx,IndexReg ;und die Timing-Daten in einer ;!
- cld ;!
- OutStringLoop0: ;Schleife in den Chip schieben. ;!
- mov al,ah ;!
- out dx,al ;Zuerst die Nummer des Registers ;!
- inc dx ;ins IndexReg, ;!
- lods byte ptr cs:[si] ;dann das nächste Timing-Byte holen ;!
- out dx,al ;und ins DataReg schreiben. ;!
- dec dx ;!
- inc ah ;!
- cmp ah,14 ;!
- jne OutStringLoop0 ;Noch weitere Timing-Daten? ;!
- pop si ;!
- pop dx ;!
- pop ax ;!
- popf ;!
- int 3 ;!
- pushf ;!
- push ax ;!
- push dx ;!
- push si ;!
- mov dx,ModeReg ;!
- mov al,00101011b ;!
- out dx,al ;!
- mov si,OFFSET GraphModeTiming ;!
- mov ah,0 ;!
- mov dx,IndexReg ;und die Timing-Daten in einer ;!
- cld ;!
- OutStringLoop1: ;Schleife in den Chip schieben. ;!
- mov al,ah ;!
- out dx,al ;Zuerst die Nummer des Registers ;!
- inc dx ;ins IndexReg, ;!
- lods byte ptr cs:[si] ;dann das nächste Timing-Byte holen ;!
- out dx,al ;und ins DataReg schreiben. ;!
- dec dx ;!
- inc ah ;!
- cmp ah,14 ;!
- jne OutStringLoop1 ;Noch weitere Timing-Daten? ;!
- pop si ;!
- pop dx ;!
- pop ax ;!
- popf ;!
- ret ;!
- BreakPoint ENDP ;!
- ;!
- MarkOn PROC NEAR ;!
- push es ;!
- push ax ;!
- mov al,23h ;!
- out 61h,al ;!
- mov ax,0B000h ;!
- mov es,ax ;!
- pop ax ;!
- mov byte ptr es:[159],7 ;!
- inc byte ptr es:[158] ;!
- pop es ;!
- ret ;!
- MarkOn ENDP ;!
- ;!
- MarkOff PROC NEAR ;!
- push es ;!
- push ax ;!
- mov al,20h ;!
- out 61h,al ;!
- mov ax,0B000h ;!
- mov es,ax ;!
- pop ax ;!
- mov byte ptr es:[159],7 ;!
- dec byte ptr es:[158] ;!
- pop es ;!
- ret ;!
- MarkOff ENDP ;!
- ;----------------------------------------------------------------------------+
- ENDIF
-
- ;+---------------------------------------------------------------------------+
- ;! Der Teil ab hier wird nach dem Installieren wieder freigegeben. !
- ;+---------------------------------------------------------------------------+
- ASSUME CS:TheProgram,DS:TheProgram,ES:TheProgram,SS:TheProgram
-
- RotateBuffer LABEL BYTE
- MyInt10Stack EQU RotateBuffer+BufSize
- MyInt5Stack EQU MyInt10Stack+StackSize
- EndOfProgram EQU MyInt5Stack+StackSize
- Card_Unknown EQU 0
- Card_Mono EQU 1
- Card_Herkules EQU 2
- VideoCard DB ?
- VideoMode DB ?
- ExplainFlag DB LOW False
- WhenTimeOut DW ?,?
-
- Main PROC NEAR
- call CheckHardware
- mov VideoCard,al
- mov VideoMode,ah
- mov si,80h ;Ab [PSP:80h] befinden sich die Aufruf-Parameter.
- call ProcessCommandLine
- cmp ExplainFlag,True ;Sollen nur Infos ausgegeben werden?
- je GetInfos
- jmp Initialisation
- Main ENDP ;Dann Fall-Through nach:
-
- GetInfos PROC NEAR
- mov dx,OFFSET ExplainMsg ;Erklärungs-Message ausgeben.
- mov ah,9 ;FunctionCall PRINT STRING ;Sie endet mit den Worten:
- int 21h ;"Checking Hardware:"...
- mov ah,Ret_Expl
- mov al,VideoCard
- cmp al,Card_Unknown
- mov dx,OFFSET UnknownMsg ;Mal sehen: Was für eine
- je MessageAndStop ;Graphik-Karte haben wir
- cmp al,Card_Mono ;hier denn?
- mov dx,OFFSET MonoCardMsg
- je MessageAndStop
- mov ah,9 ;FunctionCall PRINT STRING ;Eine Herkules? Dann
- mov dx,OFFSET HerkCardMsg ;testen wir weiter:
- int 21h
- call TestIfResident ;Ist dieses Programm
- mov ah,Ret_Expl ;schon resident?
- mov dx,OFFSET NotResiMsg
- jne MessageAndStop ;Nein, noch nicht.
- mov dx,OFFSET ResidentMsg
- mov ah,9 ;FunctionCall PRINT STRING
- int 21h
- mov ah,Ret_Expl
- mov dx,OFFSET OnMsg
- cmp es:FastScrollFlag,True
- je MessageAndStop
- mov dx,OFFSET OffMsg
- GetInfos ENDP ;Schon wieder Fall-Through:
-
- MessageAndStop PROC NEAR ;[DS:DX]=Message, AH=ReturnCode.
- push ax
- mov ah,9 ;FunctionCall PRINT STRING
- int 21h
- pop ax
- mov al,ah
- mov ah,4Ch ;FunctionCall TERMINATE PROCESS
- int 21h
- MessageAndStop ENDP
-
- RemoveFromMemory PROC NEAR ;[DX:0]=RotateBuffer, [ES:0]=residentes HERKULES.
- cmp VideoMode,8 ;Sind wir z.Z. im Graphik-Modus?
- jne LegalVidMode
- mov ah,0
- mov al,7 ;Dann auf jeden Fall in den Text-Modus zurück-
- int 10h ;schalten, sonst stehen wir nacher im Dunkeln.
- LegalVidMode:
- mov ah,5 ;Das tun wir auch, wenn
- mov al,0 ;jetzt gerade die zweite
- int 10h ;Page aktiv ist.
- mov ah,25h ;FunctionCall SET VECTOR
- lds dx,dword ptr es:SavedInt5Vec ;Int5 (PrtScr)-Vektor
- mov al,5h ;auf alten Bios-Treiber
- int 21h ;zurückbiegen,
- mov ah,25h ;FunctionCall SET VECTOR
- lds dx,dword ptr es:SavedInt10Vec ;ebenso Int10-Vektor.
- mov al,10h
- int 21h
- mov dx,ConfigReg ;Ach ja, man sollte die Konfiguration
- mov al,HGC_Diag ;auch noch auf "DIAG" stellen, damit
- out dx,al ;die Karte wieder MGA-kompatibel ist.
- nop
- push es:[2Ch] ;Adresse des Environment-Segments merken.
- mov ah,49h ;FunctionCall FREE MEMORY ;Speicher des residenten
- int 21h ;HERKULES' freigeben.
- pop es
- mov ah,49h ;FunctionCall FREE MEMORY ;Speicher des Environment-
- int 21h ;Segments freigeben.
- mov ah,35h ;FunctionCall GET VECTOR
- mov al,1Fh
- int 21h ;Wohin zeigt der Vektor $1F
- cmp bx,OFFSET ZeichenSatz8x8 ;(der Zeiger auf die obere Hälfte
- jne GrafTablNotLoaded ;des IBM-ASCII-Zeichensatzes
- mov ax,es ;für CGA-GraphikModi)?
- mov dx,cs
- cmp ax,dx ;Auf unseren eingebauten ZeichenSatz?
- jne GrafTablNotLoaded
- xor dx,dx ;Dann diesen Vektor
- mov ds,dx ;auf NIL zurücksetzen,
- mov ah,25 ;FunctionCall SET VECTOR ;sonst gibt der nacher noch
- mov al,1Fh ;Zeichen aus dem ZeichenSatz aus,
- int 21h ;der schon gar nicht mehr existiert.
- GrafTablNotLoaded:
- mov ah,Ret_Ok
- mov dx,cs
- mov ds,dx
- mov dx,OFFSET RemovedMsg
- jmp MessageAndStop
- RemoveFromMemory ENDP
-
- Initialisation PROC NEAR ;Neuen Video-Treiber installieren.
- call TestIfResident
- je AlreadyResident ;Schon resident?
- mov al,VideoCard ;Noch nicht! Dann sollten wir eigentlich
- cmp al,Card_Herkules ;resident werden, aber zuerst testen:
- mov dx,OFFSET InvCardMsg ;Ist das eine Herkules-Karte?
- je MakeResident ;Dann ist es ja prima.
- jmp FatalError ;NICHT? Um Gotteswillen, Fehler!
- AlreadyResident:
- mov al,FastScrollFlag
- cmp al,Dunnow ;Wurde eine der FastScroll-Options angegeben?
- je RemoveFromMemory ;Nicht? Dann Treiber aus dem Speicher werfen.
- cmp es:FastScrollFlag,TRUE ;Ist FastScrolling momentan an?
- mov es:FastScrollFlag,al
- jne UpdateDone
- cmp al,False ;Und soll es jetzt ausgeschaltet werden ("/S")?
- jne UpdateDone
- mov al,VideoMode
- cmp al,8 ;Und sind wir außerdem noch im Graphik-Modus?
- jne UpdateDone
- mov ah,00h ;SET VIDEO MODE ;Dann muß vorher der DispStart
- int 10h ;wieder auf 0 gesetzt werden.
- UpdateDone:
- mov si,OFFSET PrnInfo
- mov di,si
- mov cx,PrnInfoSize ;(Eventuell neue) Drucker-Daten
- cld ;von hier in das residente
- rep movsb ;Programm übertragen.
- mov ah,Ret_Ok
- mov dx,OFFSET UpdatedMsg
- jmp MessageAndStop
- Initialisation ENDP
-
- MakeResident PROC NEAR
- cmp FastScrollFlag,Dunnow ;Keine Angaben zu FastScroll?
- jne FlagIsSet
- mov FastScrollFlag,FALSE ;Dann Default=FALSE verwenden.
- FlagIsSet:
- mov dx,OFFSET EndOfProgram
- cmp ds:[6],dx ;Genug Platz für RotateBuffer?
- mov dx,OFFSET OutOfMemMsg
- jb FatalError ;Nicht? Dann Abbruch.
- mov ah,25h ;FunctionCall SET VECTOR
- mov al,5h
- mov dx,OFFSET NewInt5Handler ;INT 5 auf dieses
- int 21h ;Programm umlenken,
- mov ah,25h ;FunctionCall SET VECTOR
- mov al,10h ;genauso INT 10.
- mov dx,OFFSET NewInt10Handler
- int 21h
- mov dx,ConfigReg ;Da dieser INT10-Handler auch die zweite
- mov al,HGC_Text ;VideoPage unterstützt, wird diese
- out dx,al ;jetzt in der Herkules-Karte enabled.
- push ds
- mov ax,BiosData
- mov ds,ax ;Außerdem wird die Nummer der letzten Zeile im
- ASSUME DS:BiosData ;BiosData-Segment auf 24 gesetzt, das alte Bios
- mov LastTextLine,24 ;unterstützt diese Variable nämlich noch nicht.
- mov bl,7
- mov bh,1
- mov cx,0 ;Jetzt sollten wir noch die zweite,
- mov dx,25*256+80 ;bis jetzt ja weder benutzte noch initialisierte
- call ClearWindow ;Page löschen.
- pop ds
- ASSUME DS:TheProgram
- mov ah,35h ;FunctionCall GET VECTOR
- mov al,1Fh ;Vektor $1F zeigt im auf einen
- int 21h ;ZeichenSatz mit den oberen 128
- cmp bx,0 ;IBM-ASCII-Zeichen für die CGA-
- jne GrafTablAlreadyLoaded ;Graph-Modi. Ist dieser Zeichen-
- mov ax,es ;Satz schon geladen, ist der
- cmp ax,0 ;Zeiger ungleich NIL?
- jne GrafTablAlreadyLoaded
- mov ah,25h ;FunctionCall SET VECTOR ;Dann Zeiger auf eigenen ZeichenSatz
- mov al,1Fh ;setzen, das bringt zwar nur mit
- mov dx,OFFSET ZeichenSatz8x8+128*8 ;einem CGA-Simulator etwas, kostet
- int 21h ;aber keinen Speicherplatz und deshalb...
- GrafTablAlreadyLoaded:
- mov ah,9 ;FunctionCall PRINT STRING
- mov dx,OFFSET HerkCardMsg
- int 21h ;Dann entsprechende
- mov dx,OFFSET SuccessMsg ;Message ausgeben,
- int 21h
- mov ah,31h ;FunctionCall KEEP PROCESS
- mov al,Ret_Ok
- mov dx,OFFSET EndOfProgram+15
- mov cl,4
- shr dx,cl
- cld
- int 21h ;und resident werden.
- MakeResident ENDP
-
- FatalError PROC NEAR ;[DS:DX]=ErrorMessage.
- push dx
- mov dx,OFFSET FatalErrMsg
- mov ah,9 ;FunctionCall PRINT STRING
- int 21h
- pop dx
- mov ah,Ret_Error
- jmp MessageAndStop
- FatalError ENDP
-
- ProcessCommandLine PROC NEAR ;[DS:SI]=CommandLine
- cld
- lodsb ;Längen-Byte lesen
- mov cl,al
- mov ch,0 ;und nach CX holen.
- ParameterLoop:
- jcxz EoCommandLine ;CX=0 => Ende der CmdLine erreicht.
- lodsb ;Zeichen holen,
- dec cx
- cmp al,' '
- je ParameterLoop
- cmp al,'?' ;ein Fragezeichen?
- jne OtherParam
- mov ExplainFlag,True ;Dann Flag setzen
- jmp SHORT ParameterLoop ;und nächsten Parameter bearbeiten.
- OtherParam:
- cmp al,OptChar ;Ist es ein "/"?
- mov dx,OFFSET InvParamMsg
- jne FatalError ;Auch nicht? => Fehler.
- mov dx,OFFSET InvOptionMsg ;CommandLine hinter "/" zuende?
- jcxz FatalError ;=> Invalid Option.
- lodsb
- dec cx ;Zeichen hinter "/" holen
- cmp al,'a'
- jb IsUpcase
- cmp al,'z' ;und nach Großschrift wandeln.
- ja IsUpcase
- sub al,'a'-'A'
- IsUpcase:
- cmp al,'F' ;Ist es ein "F"?
- mov ah,True ;Dann Flag setzen für "Fast Scroll".
- je SetScrollFlag
- cmp al,'S'
- mov ah,False ;Ist es ein "S"? Dann das Flag löschen.
- je SetScrollFlag
- jmp FatalError ;Weder "F" noch "S"? => Invalid Option.
- SetScrollFlag:
- mov FastScrollFlag,ah
- jmp SHORT ParameterLoop
- EoCommandLine:
- ret
- ProcessCommandLine ENDP
-
- CheckHardware PROC NEAR
- mov ah,0Fh ;Get Video Mode
- int 10h ;Was für ein Video-Mode ist das denn?
- push ax
- mov ah,Card_Unknown
- cmp al,7 ;VideoMode 7 oder 8? Dann könnte das durchaus
- je PossiblyHerkulesCard ;eine Herkules Karte sein, muß aber
- cmp al,8 ;noch genauer getestet werden.
- jne CheckingDone ;Weder 7 noch 8? => Unbekannte Karte.
- PossiblyHerkulesCard: ;Es KÖNNTE also eine sein, muß näher testen:
- mov dx,BiosData
- mov es,dx ;Port-Adresse der Video-Karte mit der
- ASSUME ES:BiosData
- cmp es:VidCtrlIoAdr,IndexReg ;der Herkules und MDA vergleichen:
- ASSUME ES:NOTHING
- jne CheckingDone ;Ungleich? Dann unbekannte Karte.
- mov ah,0 ;Read Ticker-Count
- int 1Ah ;Uhrzeit in 1/18-Sekunden nach CX:DX holen,
- add dx,9 ;9/18 = ½ Sekunde dazu addieren.
- adc cx,0
- mov WhenTimeOut,dx
- mov WhenTimeOut+2,cx
- WaitLoop:
- mov cx,10000 ;10000 mal nacheinander
- mov dx,StatusReg ;das Status-Register
- in al,dx ;auslesen:
- and al,80h
- mov ah,al
- CloseLoop: ;Wenn sich das MSB
- in al,dx ;(Horizontal Retrace)
- and al,80h ;ändert,
- cmp al,ah
- loope CloseLoop
- mov ah,Card_Herkules
- jne CheckingDone ;dann ist das eine Herkules-Karte.
- mov ah,0 ;Read Ticker-Count
- int 1Ah ;Zeit abfragen:
- cmp cx,WhenTimeOut+2 ;Ist die ½ Sekunde vorbei?
- jb WaitLoop ;Noch nicht? Dann weiter warten.
- mov ah,Card_Mono
- ja CheckingDone ;Schon vorbei? Dann ist das wohl keine Herkules.
- cmp dx,WhenTimeOut
- jb WaitLoop
- CheckingDone:
- mov al,ah
- pop dx
- mov ah,dl
- ret
- CheckHardware ENDP ;AL=Karten-Kennung, AH=VideoMode.
-
- TestIfResident PROC NEAR ;Sind wir schon resident?
- mov ah,35h ;FunctionCall GET VECTOR
- mov al,5h
- int 21h
- mov SavedInt5Vec,bx ;Vektor für INT 5 (PrtScr)
- mov SavedInt5Vec+2,es ;nach ES:BX holen und merken.
- mov ah,35h ;FunctionCall GET VECTOR
- mov al,10h ;Vektor für INT 10h
- int 21h ;nach ES:BX holen
- mov SavedInt10Vec,bx ;und merken.
- mov SavedInt10Vec+2,es ;Stimmt der Ofs des INT10-Vektors
- cmp bx,OFFSET NewInt10Handler ;mit dem des INT10-Handlers überein?
- jne NotResident ;Nicht? Dann sind wir noch nicht resident.
- cmp SavedInt5Vec,OFFSET NewInt5Handler ;Genauso die Ofs beim
- jne NotResident ;INT5 vergleichen...
- mov ax,SavedInt10Vec+2 ;Und die Segs vom INT 5-
- cmp ax,SavedInt5Vec+2 ;und INT 10-Handler?
- jne NotResident
- mov si,OFFSET NewInt10Handler+1 ;Um ganz sicher zu gehen, vergleichen
- mov di,si ;wir auch noch die Version-Strings.
- mov cx,17
- cld
- rep cmpsb ;Stimmen beide überein?
- NotResident:
- ret
- TestIfResident ENDP ;ZF=1 wenn schon resident. ES = SEG(residenterHandler).
-
- UnknownMsg DB '*** Unknown Graphics Card ***',13,10,'$'
- MonoCardMsg DB '*** IBM Monochrome Card detected ***',13,10,'$'
- HerkCardMsg DB '*** HGC detected, HERKULES $'
- SuccessMsg DB 'made resident ***',13,10,'$'
- NotResiMsg DB 'not yet resident ***',13,10,'$'
- ResidentMsg DB 'already loaded with FastScrolling $'
- OnMsg DB 'ON ***',13,10,'$'
- OffMsg DB 'OFF ***',13,10,'$'
- UpdatedMsg DB '*** HERKULES already resident, Options updated ***',13,10,'$'
- RemovedMsg DB '*** HERKULES removed from Memory ***',13,10,'$'
- FatalErrMsg DB '*** Fatal Error: $'
- InvCardMsg DB 'This is no Herkules Card ***',13,10,'$'
- InvParamMsg DB 'Invalid Parameter ***',13,10,'$'
- InvOptionMsg DB 'Invalid Option ***',13,10,'$'
- OutOfMemMsg DB 'Out of Memory ***',13,10,'$'
- ExplainMsg DB 'This Program is an extension to the INT 10h',13,10
- DB 'for support of the Herkules Graphic Card in',13,10
- DB 'Hires-Mode (720x360). It does NOT work with',13,10
- DB 'the IBM Monochrome Card! By adding the Opt-',13,10
- DB 'ions "/F" or "/S", you may specify whether',13,10
- DB 'Fast- or Slow-Scrolling is to be used. For',13,10
- DB 'FastScroll the DisplayStart-Register of the',13,10
- DB '6845 will be changed, which might interfere',13,10
- DB 'to some Programs. Checking Hardware:',13,10,'$'
- ;-----------------------------------------------------------------------------
- TheProgram ENDS
- END EntryPoint