Metropoli BBS
VIEWER: modex.inc MODE: TEXT (CP437)
;
; This library written by Draeden of VLA.
;
; Major thanks go to Themie Gouthas, as he is responsible for the majority
; of the screen dimentions (all except 256 wide)
;
;
; Four routines seperate this ModeX library from the others:
;
;  ResetLinear = Puts you into Mode 13h addressing again
;
;  ResetPlanar = And puts you back into planar mode
;
;  Do16  = Changes to a normal 16 color mode
;
;  Do256 = Changes back to a 256 color mode
;
;
;Use:
;    @SetModeX x360, y480, 1, 360    ;sets a 360x480x256 mode
;
;  The x360 is what width to use, the y480 is how many scan lines to use,
;  the 0 is how many scan lines each pixel uses, and the 360 is the
;  virtual width of the screen.
;
;  So, to set a "80x50x256" mode, you'd do this:
;
;    @SetModeX x320, y400, 4, 320
;
;  This sets a 320 pixel wide screen with each pixel having a height of 4.
;
;  Enjoy.

    .386
;────────────────────────────────────────────────────────────────────────────
SCANy350 = 10000000b    ;(362 active)
SCANy400 = 01000000b    ;(414 active)
SCANy480 = 11000000b    ;(496 active)

SCANx360 = 00000100b    ;(720 wide) 25-mHZ
SCANx320 = 00000000b    ;(640 wide) 28-mHZ
;────────────────────────────────────────────────────────────────────────────
_x256 LABEL WORD
    db      SCANx320    ; Clock Select (CS)
    db      06          ; Number of CRTC Registers to update
    dw      05f00h      ; horz total
    dw      03f01h      ; horz displayed
    dw      04002h      ; start horz blanking
    dw      0a003h      ; end horz blanking
    dw      04f04h      ; start h sync
    dw      00405h      ; end h sync
    
_x320 LABEL WORD
    db      SCANx320    ; Clock Select (CS)
    db      0           ; Number of CRTC Registers to update

_x360 LABEL WORD
    db      SCANx360    ; Clock Select (CS)
    db      06          ; Number of CRTC Registers to update
    dw      06b00h      ; horz total
    dw      05901h      ; horz displayed
    dw      05a02h      ; start horz blanking
    dw      08e03h      ; end horz blanking
    dw      05e04h      ; start h sync
    dw      08a05h      ; end h sync

_x376 LABEL WORD
    db      SCANx360    ; Clock Select (CS)
    db      06          ; Number of CRTC Registers to update
    dw      06e00h      ; horz total
    dw      05d01h      ; horz displayed
    dw      05e02h      ; start horz blanking
    dw      09103h      ; end horz blanking
    dw      06204h      ; start h sync
    dw      08f05h      ; end h sync
;────────────────────────────────────────────────────────────────────────────
_y308 LABEL WORD
    db      SCANy400    ; Horizontal Sync Polarity (HSP)
    db      7           ; Number of CRTC Registers to update
    dw      06206h      ; vertical total
    dw      00f07h      ; overflow (VRS, VDE, VBS, VT)
    dw      03710h      ; v sync start
    dw      08911h      ; v sync end and protect cr0-cr7
    dw      03312h      ; vertical displayed
    dw      03c15h      ; v blank start
    dw      05c16h      ; v blank end

_Y360 LABEL word
    db      SCANy480    ; Horizontal Sync Polarity (HSP)
    db      5           ; Number of CRTC Registers to update
    dw      08810h      ; v sync start
    dw      08511h      ; v sync end and protect cr0-cr7
    dw      06712h      ; vertical displayed
    dw      06d15h      ; v blank start
    dw      0ba16h      ; v blank end
    
_y400 LABEL WORD
    db      SCANy400    ; Horizontal Sync Polarity (HSP)
    db      0           ; Number of CRTC Registers to update     

_y480 LABEL WORD
    db      SCANy480    ; Horizontal Sync Polarity (HSP)
    db      7           ; Number of CRTC Registers to update
    dw      00d06h      ; vertical total
    dw      03e07h      ; overflow (VRS, VDE, VBS, VT)
    dw      0ea10h      ; v sync start
    dw      0ac11h      ; v sync end and protect cr0-cr7
    dw      0df12h      ; vertical displayed
    dw      0e715h      ; v blank start
    dw      00616h      ; v blank end
    
_y564 LABEL WORD
    db      SCANy480    ; Horizontal Sync Polarity (HSP)
    db      8           ; Number of CRTC Registers to update
    dw      06206h      ; vertical total
    dw      0f007h      ; overflow (VRS, VDE, VBS, VT)
    dw      06009h      ; set VBS bit 9
    dw      03710h      ; v sync start
    dw      08911h      ; v sync end and protect cr0-cr7
    dw      03312h      ; vertical displayed
    dw      03c15h      ; v blank start
    dw      05c16h      ; v blank end
;────────────────────────────────────────────────────────────────────────────
x256    =   0
x320    =   1
x360    =   2
x376    =   3

y308    =   0
y360    =   1
y400    =   2
y480    =   3
y564    =   4

o EQU OFFSET
s EQU SHORT

Xmodes  dw  o _x256, o _x320, o _x360, o _x376
Ymodes  dw  o _y308, o _y360, o _y400, o _y480, o _y564

NumXmodes   =   4
NumYmodes   =   5
;────────────────────────────────────────────────────────────────────────────
InputStatus1=   3dah
MISC_OUTPUT =   3c2h
READ_MISC   =   3cch
SC_Index    =   3c4h
CRTC_Index  =   3d4h
Graph_Index =   3ceh
Attr_Index  =   3c0h    ;don't forget to clear flipflop & set bit 5 on index
PEL_Write   =   3c8h
PEL_Read    =   3c7h
PEL_Data    =   3c9h

VGASeg      dw  0a000h
;────────────────────────────────────────────────────────────────────────────
;    ────────────────────────────────────────────────────────────────────
    ; A MACRO!
    ;
    ; ARG: Xmode:Byte, Ymode:Byte, Cell_Height:Byte, Virtual_Width:WORD
    ;
    ; CELL_HEIGHT: how tall each pixel is in scan lines
    ;
;    ────────────────────────────────────────────────────────────────────
@SetModeX MACRO DaXmode, DaYmode, CHeight, DaWidth
    mov     al,DaXmode
    mov     ah,DaYmode
    call    _Set_X_Mode
    mov     ax,DaWidth
    call    Set_Width
    mov     ah,CHeight
    call    Set_CellHeight
    ENDM
;    ────────────────────────────────────────────────────────────────────
    ; Set a planar mode with requested dimensions
    ;
    ; IN: AL = index into X res chart
    ;     AH = index into Y res chart
    ;
    ;OUT: CF = 1, invalid mode
    ;     CF = 0, success
;    ────────────────────────────────────────────────────────────────────
_Set_X_Mode PROC NEAR
    cmp     ah,NumYmodes
    jae     s @@InvalidMode
    cmp     al,NumYmodes
    jae     s @@InvalidMode

    jmp     s @@ValidMode

@@InvalidMode:
    stc                         ; idiot.
    ret

@@ValidMode:
    pusha
    push    es
    push    ds

    cld
    mov     bx,cs
    mov     ds,bx
    mov     es,bx

    movzx   si,al
    add     si,si
    mov     si,[si + Xmodes]    ; point to correct X data set
    movzx   bx,ah
    add     bx,bx
    mov     bx,[bx + Ymodes]    ; point to correct Y data set

    mov     ax,13h              ; let the BIOS set standard 256-color
    int     10h                 ;  mode (320x200 linear)

    mov     dx,SC_INDEX
    mov     ax,0604h
    out     dx,ax               ; disable chain4 mode

    mov     ch,[si]             ; get X clock
    or      ch,[bx]             ; get Y clock
    or      ch,00100011b        ; set PB, ER, IOA
    inc     si
    inc     bx
    
    mov     dx,READ_MISC
    in      al,dx
    cmp     al,ch
    je      s @@NoClockSet

    mov     dx,SC_INDEX
    mov     ax,0100h
    out     dx,ax               ; synchronous reset while setting Misc Output
    
    mov     dx,MISC_OUTPUT
    mov     al,ch
    out     dx,al               ; select the dot clock and Horiz
                                ;  scanning rate
    mov     dx,SC_INDEX
    mov     ax,0300h
    out     dx,ax               ; undo reset (restart sequencer)

@@NoClockSet:

    mov     dx,CRTC_INDEX       ; reprogram the CRT Controller
    mov     al,11h              ; VSync End reg contains register write
    out     dx,al               ; protect bit
    inc     dx                  ; CRT Controller Data register
    in      al,dx               ; get current VSync End register setting
    and     al,07fh             ; remove write protect on various
    out     dx,al               ; CRTC registers
    dec     dx                  ; CRT Controller Index

    mov     ax,00014h           ; turn off dword mode
    out     dx,ax
    mov     ax,0e317h           ; turn on byte mode
    out     dx,ax

    lodsb                       ; lets do the X stuff
    movzx   cx,al
    jcxz    @@NoX

@@SetCRTParmsLoop:
    lodsw                       ; do the next CRT Index/Data pair
    out     dx,ax
    loop    @@SetCRTParmsLoop

@@NoX:
    mov     si,bx
    lodsb
    movzx   cx,al
    jcxz    @@NoY

@@SetCRTParmsLoop2:
    lodsw                       ; do the next CRT Index/Data pair
    out     dx,ax
    loop    @@SetCRTParmsLoop2

@@NoY:

    mov     dx,SC_INDEX
    mov     ax,0f02h
    out     dx,ax               ; enable writes to all four planes

                                ; now clear all display memory, 8 pixels
    mov     es,[VGAseg]         ; at a time
    xor     di,di               ; point ES:DI to display memory
    xor     ax,ax               ; clear to zero-value pixels
    mov     cx,8000h            ; # of words in display memory
    rep     stosw               ; clear all of display memory
    
    pop     ds
    pop     es
    popa
    clc                         ; success!
    ret
_Set_X_Mode ENDP
;────────────────────────────────────────────────────────────────────────────
;*   MISC planar mode routines
;────────────────────────────────────────────────────────────────────────────
;    ────────────────────────────────────────────────────────────────────
    ; Sets logical width of screen to whats in CX
;    ────
    ; IN: AX = desired width
    ;
    ;OUT: AX = real width
;    ────────────────────────────────────────────────────────────────────
Set_Width PROC NEAR
    push    dx
    shr     ax,3            ; divide by 8
    mov     ah,al           ; move value into AH
    mov     dx,CRTC_INDEX   ; get the PORT
    mov     al,13h          ; and the INDEX
    out     dx,ax           ; set it
    movzx   ax,ah           ; put value back in AX
    shl     ax,3            ; mult by 8
    pop     dx
    ret
Set_Width ENDP
;    ────────────────────────────────────────────────────────────────────
    ; Sets the MSL register.  It's the number of scan lines the
    ; data is repeated before advancing to the next data line. 
    ; (Char Height in TEXT mode.)
;    ────
    ; IN: AH = Cell height
    ;
    ;OUT: AX is destoryed
;   ────────────────────────────────────────────────────────────────────
Set_CellHeight PROC NEAR
    push    dx
    dec     ah              ;Cell height - 1
    mov     dx,CRTC_Index
    mov     al,9
    out     dx,al
    inc     dl
    in      al,dx
    and     al,11100000b
    or      al,ah
    out     dx,al
    pop     dx
    ret
Set_CellHeight ENDP
;    ────────────────────────────────────────────────────────────────────
    ; Latches the planes to be read from/written to
    ;
    ; NOTE: After calling this routine once, you can set the latch by
    ;       doing an OUT to PORT SC_Index + 1 with the plane mask.
    ;       This works until SC_Index is changed.
;    ────
    ; IN: AH = plane mask - bit 0 = plane 0, 1 = 1, etc.. upto 4
    ;
    ;OUT: AL is destroyed
;    ────────────────────────────────────────────────────────────────────
Latch_Planes PROC NEAR
    push    dx
    mov     dx,SC_Index
    mov     al,2
    out     dx,ax
    pop     dx
    ret
Latch_Planes ENDP
;    ────────────────────────────────────────────────────────────────────
    ; Latches a plane for READ operation (only for READ MODE #0)
;    ────
    ; IN: AH = plane to latch for read (0-3)
    ;
    ;OUT: AL is destroyed
;    ────────────────────────────────────────────────────────────────────
Set_Read_Plane PROC NEAR
    push    dx
    mov     dx,Graph_Index
    mov     al,4
    out     dx,ax
    pop     dx
    ret
Set_Read_Plane ENDP
;    ────────────────────────────────────────────────────────────────────
    ; Sets WRITE mode (0-3)
    ;
    ; WM0:  Data from BUS goes to Video Memory (all latched planes)
    ;
    ; WM1:  Data is read into 32bit latch and written from 32bit latch
    ;       BUS data has no effect.  Allows data from all 4 planes to 
    ;       be moved from one place in VGA to another.
    ;
    ; WM2 & WM3:  Look them up.
    ;
;    ────
    ; IN: AH = WRITE mode (0-3)
    ;
    ;OUT: AL is destroyed
;    ────────────────────────────────────────────────────────────────────
Set_Write_Mode PROC NEAR
    push    dx
    mov     dx,Graph_Index
    mov     al,5
    out     dx,al
    inc     dx
    in      al,dx
    and     al,11111100b    ;clear out write mode bits
    or      al,ah
    out     dx,al
    pop     dx
    ret
Set_Write_Mode ENDP
;    ────────────────────────────────────────────────────────────────────
    ; Sets the READ mode
    ;
    ; RM0 = Normal one. Reads data.
    ;
    ; RM1 = Weird one. Reads results of comparison.
;    ────
    ; IN: AH = read mode (0 or 1)
    ;
    ;OUT: AX is destroyed
;    ────────────────────────────────────────────────────────────────────
Set_Read_Mode PROC NEAR
    push    dx
    mov     dx,Graph_Index
    mov     al,5
    out     dx,al
    inc     dx
    in      al,dx
    and     al,11110111b    ;clear out write mode bits
    shl     ah,3            ;move bit to correct position
    and     ah,00001000b
    or      al,ah
    out     dx,al
    pop     dx
    ret
Set_Read_Mode ENDP
;    ────────────────────────────────────────────────────────────────────
    ; Sets starting offset - useful for panning or using multiple pages.
    ;
;    ────
    ; IN: BX = Offset
    ;
    ;OUT: AX is destoryed
;    ────────────────────────────────────────────────────────────────────
Set_Start_Offset PROC NEAR
    push    dx
    mov     dx,CRTC_Index
    mov     al,0ch
    mov     ah,bh       ;write the HIGH byte
    out     dx,ax
    inc     al
    mov     ah,bl       ;write the LOW byte
    out     dx,ax
    pop     dx
    ret
Set_Start_Offset ENDP
;    ────────────────────────────────────────────────────────────────────
    ; Sets the pelpan value - useful for smooth sideways panning
    ;
    ; NOTE: DO NOT USE ODD VALUES except in text mode or 16 color mode
    ;
;    ────
    ; IN: AH = pelpan value
    ;
    ;OUT: AL is destoryed
;    ────────────────────────────────────────────────────────────────────
Set_HPP PROC NEAR
    push    dx
    mov     dx,InputStatus1
    in      al,dx           ;dummy input
    mov     dx,Attr_Index
    mov     al,33h
    out     dx,al
    mov     al,ah
    out     dx,al
    pop     dx
    ret
Set_HPP ENDP
;    ────────────────────────────────────────────────────────────────────
    ; Sets pixel pan compatibility - makes it so the HPP value does not
    ;  alter the split screen.
;    ────────────────────────────────────────────────────────────────────
Set_PPC PROC NEAR
    push    ax
    push    dx
    mov     dx,InputStatus1
    in      al,dx           ;dummy input
    mov     dx,Attr_Index
    mov     al,30h
    out     dx,al
    inc     dx
    in      al,dx
    dec     dx
    or      al,00100000b
    out     dx,al
    pop     dx
    pop     ax
    ret
Set_PPC ENDP
;    ────────────────────────────────────────────────────────────────────
    ; Sets the split screen scan line
;    ────
    ; IN: BX = Scan line to set split at
    ;
    ;OUT: None
;    ────────────────────────────────────────────────────────────────────
Set_Split PROC NEAR
    push    dx
    push    ax
    mov     al,18h
    mov     ah,bl
    mov     dx,CRTC_Index
    out     dx,ax       ;set bits 0-7

    mov     al,09h
    out     dx,al
    inc     dx
    in      al,dx
    mov     ah,bh
    and     ah,00000010b
    shl     ah,5
    and     al,10111111b
    or      al,ah
    out     dx,al       ;set bit 9

    dec     dx
    mov     al,07h
    out     dx,al
    inc     dx
    in      al,dx
    and     al,11101111b
    mov     ah,bh
    and     ah,00000001b
    shl     ah,4
    or      al,ah
    out     dx,al       ;set bit 8
    pop     ax
    pop     dx
    ret
Set_Split ENDP
;    ────────────────────────────────────────────────────────────────────
    ; Waits until verticle retrace starts and then waits for it to end.
;    ────────────────────────────────────────────────────────────────────
Wait_FVR1 PROC NEAR
    push    dx
    push    ax
    mov     dx,InputStatus1
@@Vr:
    in      al,dx
    test    al,8
    jz      s @@Vr                  ;wait until Verticle Retrace starts
@@Nvr:
    in      al,dx
    test    al,8
    jnz     s @@Nvr                 ;wait until Verticle Retrace Ends
    pop     ax
    pop     dx
    ret
Wait_FVR1 ENDP
;    ────────────────────────────────────────────────────────────────────
    ; Waits until NON-VR time and then wait for VR to start
;    ────────────────────────────────────────────────────────────────────
Wait_FVR2 PROC NEAR
    push    dx
    push    ax
    mov     dx,InputStatus1
@@@Vr:
    in      al,dx
    test    al,8
    jnz     s @@@Vr                  ;wait until Verticle Retrace starts
@@@Nvr:
    in      al,dx
    test    al,8
    jz      s @@@Nvr                 ;wait until Verticle Retrace Ends
    pop     ax
    pop     dx
    ret
Wait_FVR2 ENDP
;    ────────────────────────────────────────────────────────────────────
    ; Waits until VR is true - MAY be missed if interrupts are enabled
;    ────────────────────────────────────────────────────────────────────
Wait_VR PROC NEAR
    push    dx
    push    ax
    mov     dx,InputStatus1
@@@@Vr:
    in      al,dx
    test    al,8
    jz      s @@@@Vr                  ;wait until Verticle Retrace starts
    pop     ax
    pop     dx
    ret
Wait_VR ENDP
;    ────────────────────────────────────────────────────────────────────
    ; Waits for a NON-VR period
;    ────────────────────────────────────────────────────────────────────
Wait_NVR PROC NEAR
    push    dx
    push    ax
    mov     dx,InputStatus1
@@@@Nvr:
    in      al,dx
    test    al,8
    jnz     @@@@Nvr                 ;wait until Verticle Retrace Ends
    pop     ax
    pop     dx
    ret
Wait_NVR ENDP
;    ────────────────────────────────────────────────────────────────────
    ; Write a specified palette chunk
;    ────
    ; IN: DS:SI = Address of palette
    ;        CX = Number of colors to write (NOT *3)
    ;        AL = Starting palette index
    ;
    ;OUT: None
;    ────────────────────────────────────────────────────────────────────
WritePalette PROC NEAR
;    push    dx cx si
    mov     dx,cx
    add     cx,cx
    add     cx,dx
    mov     dx,03c8h
    out     dx,al
    inc     dx
    cld

    rep outsb
;    pop     si cx dx
    ret
WritePalette ENDP
;    ────────────────────────────────────────────────────────────────────
    ; Puts the VGA card back into PLANAR mode at current resolutions
    ;
    ; NOTE: It is recommended that you clear ALL 256k VGA memory AFTER
    ;       resetting into PLANAR mode, as SOME VGA cards may go funky.
;    ────────────────────────────────────────────────────────────────────
ResetPlanar PROC NEAR
    push    ax
    push    dx
    mov     dx,SC_INDEX
    mov     ax,0604h
    out     dx,ax               ; disable chain4 mode
    
    mov     dx,CRTC_INDEX
    mov     ax,00014h           ; turn off dword mode
    out     dx,ax
    mov     ax,0e317h           ; turn on byte mode
    out     dx,ax
    pop     dx
    pop     ax
    ret
ResetPlanar ENDP
;    ────────────────────────────────────────────────────────────────────
    ; Changes from ModeX back to linear mode (like MODE 13h)
    ;
    ; NOTE: It is recommended that you clear ALL 256k VGA memory BEFORE
    ;       resetting into LINEAR mode, as SOME VGA cards may go funky.
;    ────────────────────────────────────────────────────────────────────
ResetLinear PROC NEAR
    push    dx
    push    ax
    mov     dx,SC_INDEX
    mov     ax,0E04h
    out     dx,ax               ; enable chain4 mode

    mov     dx,CRTC_INDEX       ; reprogram the CRT Controller
    mov     ax,04014h           ; turn on dword mode
    out     dx,ax
    mov     ax,0a317h           ; turn off byte mode
    out     dx,ax
    pop     ax
    pop     dx
    ret
ResetLinear ENDP
;────────────────────────────────────────────────────────────────────────────
;Differences between 16 color mode and X-Mode:
;
;        16 color     256 color
;
; GRAPH  5 = 00h        40h     ;Shift register is different
;
; ATTR   6 = 14h        06      ;these are palette remap differences
;        8 = 38h        08      ; so they don't matter one whit
;        9 = 39h        09
;        A = 3ah        0a
;        B = 3bh        0b
;        C = 3ch        0c
;        D = 3dh        0d
;        E = 3eh        0e
;        F = 3fh        0f
;       10 = 01h        41      ;PCS is different
;────────────────────────────────────────────────────────────────────────────
;    ────────────────────────────────────────────────────────────────────
    ;   Changes FROM 256 color planar TO 16 color planar
    ;
    ; The screen WILL screw up when the change is made, so clear it
    ;first.  Also sets the read/write mode to 0.
;    ────────────────────────────────────────────────────────────────────
Do16 PROC NEAR
    push    ax
    push    dx

    mov     dx,Graph_Index
    mov     ax,00005h           ;set Shift reg to 0
    out     dx,ax

    mov     dx,InputStatus1
    in      al,dx               ;dummy input
    mov     dx,Attr_Index
    mov     al,30h
    out     dx,al
    mov     al,21h              ;turn off PCS, and turns on PCC
    out     dx,al

    mov     ah,0
    call    Set_Hpp

    pop     dx
    pop     ax
    ret
Do16 ENDP
;    ────────────────────────────────────────────────────────────────────
    ;   Changes FROM 16 color planar TO 256 color planar
    ;
    ; The screen WILL screw up when the change is made, so clear it
    ;first.  Also sets the read/write mode to 0.
;    ────────────────────────────────────────────────────────────────────
Do256 PROC NEAR
    push    ax
    push    dx

    mov     dx,Graph_Index
    mov     ax,04005h           ;set Shift reg to 1
    out     dx,ax

    mov     dx,InputStatus1
    in      al,dx               ;dummy input
    mov     dx,Attr_Index
    mov     al,30h
    out     dx,al
    mov     al,61h              ;turns on PCS & PCC
    out     dx,al

    mov     ah,0
    call    Set_Hpp

    pop     dx
    pop     ax
    ret
Do256 ENDP
[ RETURN TO DIRECTORY ]