;-----------------------------------------------------------
;
; MXSM.ASM - Set/change mode functions
; Copyright (c) 1993,1994 by Alessandro Scotti
;
;-----------------------------------------------------------
WARN    PRO
INCLUDE MODEX.DEF

PUBLIC  mxChangeMode
PUBLIC  mxGetAspect
PUBLIC  mxGetScreenSize
PUBLIC  mxSetMode

PUBLIC  mx_ScreenWidth
PUBLIC  mx_ScreenHeight
PUBLIC  mx_BytesPerLine

MX_TEXT         SEGMENT USE16 PARA PUBLIC 'CODE'
                ASSUME cs:MX_TEXT, ds:NOTHING, es:NOTHING

EXTRN   mxSetSysClipRegion      : FAR

EXTRN   mx_VideoSegment : WORD
EXTRN   mx_CodeSegment  : WORD

mx_ScreenWidth  DW      ?               ; Current screen width
mx_ScreenHeight DW      ?
mx_AspectX      DW      ?               ; Aspect ratio for current mode
mx_AspectY      DW      ?
mx_BytesPerLine DW      0               ; Bytes per line

;
; Tables for setting video modes, sources:
; - MODEX.ASM, Matt Pritchard
; - Dr. Dobb's Journal, Michael Abrash
; - Fractint VIDEO.ASM module
;
TBL_SingleLine  LABEL   WORD            ; CRTC
        DW      04009h                  ; Cell height: 1 scan line
        DW      00014h                  ; Double word mode off
        DW      0E317h                  ; Byte mode on
        DW      0
TBL_DoubleLine  LABEL   WORD            ; CRTC
        DW      04109h                  ; Cell height: 2 scan lines
        DW      00014h
        DW      0E317h
        DW      0
TBL_Width320    LABEL   WORD            ; CRTC
        DW      05F00h                  ; Horizontal total
        DW      04F01h                  ; Horizontal displayed
        DW      05002h                  ; Start horizontal blanking
        DW      08203h                  ; End horizontal blanking
        DW      05404h                  ; Start horizontal sync
        DW      08005h                  ; End horizontal sync
        DW      02813h                  ; Row address
        DW      0
TBL_Width360    LABEL   WORD            ; CRTC
        DW      06B00h                  ; Horizontal total
        DW      05901h                  ; Horizontal displayed
        DW      05A02h                  ; Start horizontal blanking
        DW      08E03h                  ; End horizontal blanking
        DW      05E04h                  ; Start horizontal sync
        DW      08A05h                  ; End horizontal sync
        DW      02D13h                  ; Row address
        DW      0
TBL_Height175   LABEL   WORD            ; CRTC
        DW      0BF06h                  ; Vertical total
        DW      01F07h                  ; Overflow
        DW      08310h                  ; Start vertical sync
        DW      08511h                  ; End vertical sync
        DW      05D12h                  ; Vertical displayed
        DW      06315h                  ; Start vertical blanking
        DW      0BA16h                  ; End vertical blanking
        DW      0
TBL_Height200   LABEL   WORD            ; CRTC
        DW      0BF06h                  ; Vertical total
        DW      01F07h                  ; Overflow
        DW      09C10h                  ; Start vertical sync
        DW      08E11h                  ; End vertical sync
        DW      08F12h                  ; Vertical displayed
        DW      09615h                  ; Start vertical blanking
        DW      0B916h                  ; End vertical blanking
        DW      0
TBL_Height240   LABEL   WORD            ; CRTC
        DW      00D06h                  ; Vertical total
        DW      03E07h                  ; Overflow
        DW      0EA10h                  ; Start vertical sync
        DW      08C11h                  ; End vertical sync
        DW      0DF12h                  ; Vertical displayed
        DW      0E715h                  ; Start vertical blanking
        DW      00616h                  ; End vertical blanking
        DW      0
TBL_Tweak400x600:
        DW      07400h
        DW      06301h
        DW      06402h
        DW      09703h
        DW      06804h
        DW      09505h
        DW      08606h
        DW      0F007h
        DW      06009h
        DW      0310Fh
        DW      05B10h
        DW      08D11h
        DW      05712h
        DW      03213h
        DW      00014h
        DW      06015h
        DW      08016h
        DW      0E317h
        DW      0

TBL_320x200:
        DB      63h                     ; 400 scan lines, 25 MHz clock
        DW      6, 5                    ; Aspect: 6/5 = 1.2:1
        DW      320, 200                ; Size
        DW      TBL_Width320, TBL_Height200, TBL_DoubleLine, 0
        DW      819                     ; Max height
TBL_320x240:
        DB      0E3h                    ; 400 scan lines, 25 MHz clock
        DW      1, 1                    ; Aspect: 1/1 = 1:1
        DW      320, 240                ; Size
        DW      TBL_Width320, TBL_Height240, TBL_DoubleLine, 0
        DW      819                     ; Max height
TBL_320x400:
        DB      63h                     ; 480 scan lines, 25 MHz clock
        DW      6, 10                   ; Aspect: 6/10 = 0.6:1
        DW      320, 400                ; Size
        DW      TBL_Width320, TBL_Height200, TBL_SingleLine, 0
        DW      819                     ; Max height
TBL_320x480:
        DB      0E3h                    ; 480 scan lines, 25 MHz clock
        DW      1, 2                    ; Aspect: 1/2 = 0.5:1
        DW      320, 480                ; Size
        DW      TBL_Width320, TBL_Height240, TBL_SingleLine, 0
        DW      819                     ; Max height
TBL_360x200:
        DB      067h                    ; 400 scan lines, 28 MHz clock
        DW      27, 20                  ; Aspect: 27/20 = 1.35:1
        DW      360, 200                ; Size
        DW      TBL_Width360, TBL_Height200, TBL_DoubleLine, 0
        DW      728                     ; Max height
TBL_360x240:
        DB      0E7h                    ; 480 scan lines, 28 MHz clock
        DW      9, 8                    ; Aspect: 9/8 = 1.125:1
        DW      360, 240                ; Size
        DW      TBL_Width360, TBL_Height240, TBL_DoubleLine, 0
        DW      728                     ; Max height
TBL_360x400:
        DB      067h                    ; 400 scan lines, 28 MHz clock
        DW      27, 40                  ; Aspect: 27/40 = 0.675:1
        DW      360, 400                ; Size
        DW      TBL_Width360, TBL_Height200, TBL_SingleLine, 0
        DW      728                     ; Max height
TBL_360x480:
        DB      0E7h                    ; 480 scan lines, 28 MHz clock
        DW      9, 16                   ; Aspect: 9/16 = 0.5625:1
        DW      360, 480                ; Size
        DW      TBL_Width360, TBL_Height240, TBL_SingleLine, 0
        DW      728                     ; Max height
TBL_320x175:
        DB      0A3h
        DW      0, 0                    ; Aspect:
        DW      320, 175
        DW      TBL_Width320, TBL_Height175, TBL_DoubleLine, 0
        DW      819
TBL_320x350:
        DB      0A3h
        DW      0, 0                    ; Aspect:
        DW      320, 175
        DW      TBL_Width320, TBL_Height175, TBL_SingleLine, 0
        DW      819
TBL_360x175:
        DB      0A7h
        DW      0, 0                    ; Aspect:
        DW      360, 480                ; Size
        DW      TBL_Width360, TBL_Height175, TBL_DoubleLine, 0
        DW      728                     ; Max height
TBL_360x350:
        DB      0A7h
        DW      0, 0                    ; Aspect:
        DW      360, 480                ; Size
        DW      TBL_Width360, TBL_Height175, TBL_SingleLine, 0
        DW      728                     ; Max height
TBL_400x600:
        DB      0E7h                    ; 28 MHz clock
        DW      1, 2                    ; Aspect: 1/2 = 0.5:1
        DW      400, 600                ; Size
        DW      TBL_Tweak400x600, 0
        DW      655                     ; Max height

TBL_Mode        LABEL   WORD
        DW      TBL_320x175
        DW      TBL_320x200
        DW      TBL_320x240
        DW      TBL_320x350
        DW      TBL_320x400
        DW      TBL_320x480
        DW      TBL_360x175
        DW      TBL_360x200
        DW      TBL_360x240
        DW      TBL_360x350
        DW      TBL_360x400
        DW      TBL_360x480
        DW      TBL_400x600

MAXVMODE        EQU     ($-OFFSET TBL_Mode) / 2

;-----------------------------------------------------------
;
; Enables 80x25 color text mode
;
subText         PROC    NEAR
        ASSUME  ds:MX_TEXT
        mov     ax, 0003h
        int     10h                     ; Call BIOS set mode

        mov     [mx_ScreenHeight], 0
        mov     [mx_BytesPerLine], 0
        ret
subText         ENDP

;-----------------------------------------------------------
;
; Enables the selected graphics mode.
;
; Input:
;       Mode    = mode to select (MX_???x???)
; Output:
;       none
;
mxSetMode       PROC    FAR
        ARG     Mode:WORD       = ARG_SIZE
        ASSUME  ds:NOTHING
        .enter  0
        .push   ds, si, es, di

; Set DS to code segment alias
        mov     ds, [mx_CodeSegment]
        ASSUME  ds:MX_TEXT

        mov     si, [Mode]
        cmp     si, MAXVMODE            ; Is it a valid mode?
        ja      @@Exit                  ; No, exit
        test    si, si                  ; Text mode?
        jnz     @@Set                   ; No, handle it

        call    subText                 ; Back to text mode
        jmp     @@Exit                  ; Exit now

; Set video mode
@@Set:
        dec     si                      ; Skip text mode
        shl     si, 1
        mov     si, TBL_Mode[si]
        cld

; Use BIOS to set 320x200x256 linear mode
        push    si                      ; Save SI
        mov     ax, 0013h
        int     10h                     ; Use BIOS to set 320x200 linear mode
        pop     si                      ; Restore SI

        mov     dx, TS
        mov     ax, 0604h
        out     dx, ax                  ; Disable chain-4 mode
        mov     ax, 0100h
        out     dx, ax                  ; Reset
        mov     dx, MISC
        lodsb
        out     dx, al                  ; New timing/size
        mov     dx, TS
        mov     ax, 0300h
        out     dx, ax                  ; Restart sequencer

; Unlock CRTC registers 0-7
        mov     dx, CRTC
        mov     al, 11h
        out     dx, al                  ; Vertical sync end register
        inc     dx
        in      al, dx
        and     al, 7Fh                 ; Clear write protect bit
        out     dx, al

        lodsw                           ; Get X aspect
        mov     [mx_AspectX], ax
        lodsw                           ; Get Y aspect
        mov     [mx_AspectY], ax
        lodsw                           ; Get screen width
        mov     [mx_ScreenWidth], ax
        shr     ax, 1
        shr     ax, 1                   ; Divide by four to get bytes per line
        mov     [mx_BytesPerLine], ax
        lodsw                           ; Get screen height
        mov     [mx_ScreenHeight], ax

; Set CRTC registers
        mov     bx, si
        mov     dx, CRTC
@@TableLoop:
        mov     si, ds:[bx]             ; DS:SI -> table of CRTC registers
        inc     bx
        inc     bx                      ; DS:BX -> offset of next table
        test    si, si                  ; Last table?
        jz      @@EndLoop               ; Yes, exit loop
@@Loop:
        lodsw                           ; Get CRTC register index and value
        test    ax, ax                  ; End of table?
        jz      @@TableLoop             ; Yes, go to next table
        out     dx, ax                  ; Set register AL to value AH
        jmp     @@Loop                  ; Get next register/value
@@EndLoop:

; Set virtual screen and system clip region
        push    [mx_ScreenWidth]
        push    WORD PTR ds:[bx]
        call    mxSetSysClipRegion

; Clear video memory
        mov     dx, TS
        mov     ax, 0F02h
        out     dx, ax                  ; Enable all planes
        mov     es, [mx_VideoSegment]
        xor     di, di
        mov     cx, 8000h
        xor     ax, ax
        rep     stosw

@@Done:
; Lock CRTC registers 0-7 (some cards need this)
        mov     dx, CRTC
        mov     al, 11h
        out     dx, al                  ; Vertical sync end register
        inc     dx
        in      al, dx
        or      al, 80h                 ; Set write protect bit
        out     dx, al

@@Exit:
        xor     ax, ax
        mov     ax, [mx_ScreenWidth]
        .pop    ds, si, es, di
        .leave  ARG_SIZE
mxSetMode       ENDP

;-----------------------------------------------------------
;
; Changes from the current mode the selected graphics mode.
;
; Input:
;       Mode    = mode to select (MX_???x???)
; Output:
;       none
; Notes:
;       this function assumes that mxSetMode and mxSetVirtualScreen
;       have been called first. View size is rearranged to match the
;       specified mode, but video memory is not cleared.
;       Differences from mxSetMode:
;       - video BIOS is not called to initialize graphics;
;       - row address register is not modified;
;       - video memory is not cleared;
;       - mx_BytesPerLine is not modified;
;       - system clip region is not modified.
;
mxChangeMode    PROC    FAR
        ARG     Mode:WORD       = ARG_SIZE
        ASSUME  ds:NOTHING
        .enter  0
        .push   ds, si, es, di

; Set DS to code segment alias
        mov     ds, [mx_CodeSegment]
        ASSUME  ds:MX_TEXT

        mov     si, [Mode]
        cmp     si, MAXVMODE            ; Is it a valid mode?
        ja      @@Exit                  ; No, exit
        test    si, si                  ; Text mode?
        jz      @@Exit                  ; Yes, exit

        dec     si                      ; Skip text mode
        shl     si, 1
        mov     si, TBL_Mode[si]
        cld

        mov     dx, TS
        mov     ax, 0604h
        out     dx, ax                  ; Disable chain-4 mode
        mov     ax, 0100h
        out     dx, ax                  ; Reset
        mov     dx, MISC
        lodsb
        out     dx, al                  ; New timing/size
        mov     dx, TS
        mov     ax, 0300h
        out     dx, ax                  ; Restart sequencer

; Unlock CRTC registers 0-7
        mov     dx, CRTC
        mov     al, 11h
        out     dx, al                  ; Vertical sync end register
        inc     dx
        in      al, dx
        and     al, 7Fh                 ; Clear write protect bit
        out     dx, al

        lodsw                           ; Get X aspect
        mov     [mx_AspectX], ax
        lodsw                           ; Get Y aspect
        mov     [mx_AspectY], ax
        lodsw                           ; Get screen width
        mov     [mx_ScreenWidth], ax
        lodsw                           ; Get screen height
        mov     [mx_ScreenHeight], ax

; Set CRTC registers
        mov     bx, si
        mov     dx, CRTC
@@TableLoop:
        mov     si, ds:[bx]             ; DS:SI -> table of CRTC registers
        inc     bx
        inc     bx                      ; DS:BX -> offset of next table
        test    si, si                  ; Last table?
        jz      @@EndLoop               ; Yes, exit loop
@@Loop:
        lodsw                           ; Get CRTC register index and value
        test    ax, ax                  ; End of table?
        jz      @@TableLoop             ; Yes, go to next table
        cmp     al, 13h                 ; Row address register?
        je      @@Loop                  ; Yes, ignore it
        out     dx, ax                  ; Set register AL to value AH
        jmp     @@Loop                  ; Get next register/value
@@EndLoop:

; Lock CRTC registers 0-7 (some cards need this)
        mov     dx, CRTC
        mov     al, 11h
        out     dx, al                  ; Vertical sync end register
        inc     dx
        in      al, dx
        or      al, 80h                 ; Set write protect bit
        out     dx, al

@@Exit:
        xor     ax, ax
        mov     ax, [mx_ScreenWidth]
        .pop    ds, si, es, di
        .leave  ARG_SIZE
mxChangeMode    ENDP

;-----------------------------------------------------------
;
; Returns the aspect ratio for the current mode.
;
; Input:
;       AspectX = pointer to aspect X
;       AspectY = pointer to aspect Y
;
; A rectangle of width AspectX and height AspectY looks like a square.
;
mxGetAspect     PROC FAR
        ARG     AspectY:DWORD,  \
                AspectX:DWORD   = ARG_SIZE
        .enter  0
        .push   ds, si
        ASSUME  ds:NOTHING

        lds     si, [AspectX]
        mov     ax, [mx_AspectX]
        mov     ds:[si], ax
        lds     si, [AspectY]
        mov     ax, [mx_AspectY]
        mov     ds:[si], ax

        .pop    ds, si
        .leave  ARG_SIZE
mxGetAspect     ENDP

;-----------------------------------------------------------
;
; Returns the current screen size.
;
; Input:
;       Width   = pointer to screen width
;       Height  = pointer to screen height
;
mxGetScreenSize PROC FAR
        ARG     SizeY:DWORD,    \
                SizeX:DWORD     = ARG_SIZE
        .enter  0
        .push   ds, si
        ASSUME  ds:NOTHING

        lds     si, [SizeX]
        mov     ax, [mx_ScreenWidth]
        mov     ds:[si], ax
        lds     si, [SizeY]
        mov     ax, [mx_ScreenHeight]
        mov     ds:[si], ax

        .pop    ds, si
        .leave  ARG_SIZE
mxGetScreenSize ENDP

MX_TEXT         ENDS
END
