'*****************************************************************************
'* INT 10H AH=00h (VGA BIOS) SET VIDEO MODE
'* Params: AL - video mode 
'* Note: - composite and tandy modes supported also
'*****************************************************************************

//DEBUG.ON 'uncomment to enable debug messages (can be slow for hi-freq events)

use module "modes" 'data to program video ports for various modes

protected function Exec as boolean


?? "[INT 10] Start video mode AL=0x";Hex(cpu.AL,2)

//variables
dim b as byte, mode as byte, seg as word, offs as word, i as integer, atr as byte
dim mode_port as byte, color_port as byte, col_count as word, row_count as word
dim buf_size as word, w as word, textmode as boolean, ClearMem as boolean
dim MemBase as integer, MemRange as integer

//video mode to set
mode=cpu.AL : ClearMem=((mode and 0x80)=0) : mode=mode and (not 0x80)
if mode>=VGA_MODE_COUNT then exit(false)

//write video port values from mode tables
pc.WritePort(0x3C2,VGA_3C2(mode)) 
pc.WritePort(0x3D4,0x11) : pc.WritePort(0x3D5,0) //remove port protection
 for i=0 to VGA_3X5_COUNT-1 : pc.WritePort(0x3D4,i) 
pc.WritePort(0x3D5,VGA_3X5(i*VGA_MODE_COUNT+mode)) : next
 for i=0 to VGA_3C5_COUNT-1 : pc.WritePort(0x3C4,i) 
pc.WritePort(0x3C5,VGA_3C5(i*VGA_MODE_COUNT+mode)) : next
 for i=0 to VGA_3CF_COUNT-1 : pc.WritePort(0x3CE,i)
pc.WritePort(0x3CF,VGA_3CF(i*VGA_MODE_COUNT+mode)) : next
 for i=0 to VGA_3C1_COUNT-1 : b=pc.ReadPort(0x3DA) 
pc.WritePort(0x3C0,i) : pc.WritePort(0x3C1,VGA_3C1(i*VGA_MODE_COUNT+mode)) : next

//get other params of new mode
textmode=b<2
select case mode
  case 0 : textmode=true //TEXT 40x25 (HALFTONE 16)
   mode_port=0 or shl(1,2) or shl(1,3) : color_port=0 : atr=7
   col_count=40 : row_count=25 : buf_size=col_count*row_count*2
  case 1 : textmode=true //TEXT 40x25 (COLOR 16)
   mode_port=0 or shl(1,3) : color_port=0 : atr=7
   col_count=40 : row_count=25 : buf_size=col_count*row_count*2
  case 2 : textmode=true //TEXT 80x25 (HALFTONE 16)
   mode_port=1 or shl(1,2) or shl(1,3) : color_port=0 : atr=7
   col_count=80 : row_count=25 : buf_size=col_count*row_count*2
  case 3 : textmode=true //TEXT 80x25 (COLOR 16)
   mode_port=1 or shl(1,3) : color_port=0 : atr=7
   col_count=80 : row_count=25 : buf_size=col_count*row_count*2
  case 4 : textmode=false //GRAPH 320x200 (COLOR 4)
   mode_port=0 or shl(1,1) or shl(1,3) : color_port=0x20 : atr=0
   col_count=40 : row_count=25 : buf_size=(col_count*row_count*8*8)/4
  case 5 : textmode=false //GRAPH 320x200 (HALFTONE 4)
   mode_port=0 or shl(1,1) or shl(1,3) or shl(1,2) : color_port=0x20 : atr=0
   col_count=40 : row_count=25 : buf_size=(col_count*row_count*8*8)/4
  case 6 : textmode=false //GRAPH 640x200 (HALFTONE 2)
   mode_port=0 or shl(1,1) or shl(1,3) or shl(1,4) : color_port=0 : atr=0
   col_count=80 : row_count=25 : buf_size=(col_count*row_count*8*8)/8
  case 7 : textmode=false //GRAPH 160x200 (COLOR 16 COMPOSITE)
   mode_port=0 or shl(1,1) or shl(1,3) or shl(1,4) : color_port=0 : atr=0
   col_count=20 : row_count=25 : buf_size=(col_count*row_count*8*8)/2
  case 8 : textmode=false //GRAPH 160x200 (COLOR 16 TANDY)
   mode_port=0 or shl(1,1) or shl(1,3) or shl(1,4) or shl(1,7) : color_port=0x80
   col_count=20 : row_count=25 : buf_size=(col_count*row_count*8*8)/2 : atr=0
  case 9 : textmode=false //GRAPH 320x200 (COLOR 16 TANDY)
   mode_port=0 or shl(1,1) or shl(1,3) or shl(1,2) or shl(1,7) : color_port=0x80
   col_count=40 : row_count=25 : buf_size=(col_count*row_count*8*8)/2 : atr=0
  case 0xA : textmode=false //GRAPH 640x200 (COLOR 4 TANDY)
   mode_port=0 or shl(1,1) or shl(1,3) or shl(1,4) or shl(1,2) or shl(1,7) 
   color_port=0xA0 : col_count=80 : row_count=25 : atr=0
   buf_size=(col_count*row_count*8*8)/4
  case 0xD : textmode=false //GRAPH 320x200 (COLOR 16 VGA/VGA)
   mode_port=0 or shl(1,1) or shl(1,3) or shl(1,2) : color_port=0 
   col_count=40 : row_count=25 : buf_size=(col_count*row_count*8*8)/8 : atr=0
  case 0xE : textmode=false //GRAPH 640x200 (COLOR 16 VGA/VGA)
   mode_port=0 or shl(1,1) or shl(1,3) or shl(1,4) or shl(1,2) or shl(1,7) 
   color_port=0 : col_count=80 : row_count=25 : atr=0
   buf_size=(col_count*row_count*8*8)/8
  case 0x10: textmode=false //GRAPH 640x350 (COLOR 16 EGA)
   mode_port=0 or shl(1,1) or shl(1,3) or shl(1,4) or shl(1,2) or shl(1,7) 
   color_port=0 : col_count=80 : row_count=25 : atr=0
   buf_size=(col_count*row_count*8*14)/8
  case 0x12: textmode=false //GRAPH 640x480(COLOR 16 VGA)
   mode_port=0 or shl(1,1) or shl(1,3) or shl(1,4) or shl(1,2) or shl(1,7) 
   color_port=0 : col_count=80 : row_count=80 : atr=0
   buf_size=(col_count*row_count*8*8)/8
  case 0x13: textmode=false //GRAPH 320x200 (COLOR 256 VGA)
   mode_port=0 or shl(1,1) or shl(1,3) or shl(1,2) : color_port=0 
   col_count=40 : row_count=25 : buf_size=(col_count*row_count*8*8) : atr=0
  case else : exit(false) 
end select

i=buf_size/1024 : buf_size=1024*iif(buf_size>(i*1024),i+1,i) //Kb-granularity

//write params to CRT
if mode<0xD then : pc.WritePort(0x3D9,color_port) 
   pc.WritePort(0x3D8,mode_port) : pc.WritePort(0x3DB,0) : end

//write params to memory
mem.Byte(0x465)=mode_port : mem.Byte(0x466)=color_port : mem.Byte(0x449)=mode
mem.Word(0x44A)=col_count : mem.Word(0x44C)=buf_size 
mem.Word(0x463)=0x3D4 //CRT port address
mem.Word(0x44E)=0 //offset from beginning of regen. buffer
mem.Byte(0x462)=0 //video page index
mem.Word(0x450)=0 //cursor position
mem.Byte(0x460)=6 //top cursor line
mem.Byte(0x461)=7 //bottom cursor line
mem.Byte(0x484)=row_count-1 //max row index (EGA/VGA)

//clear video buffer (and fill with attribute for text mode)
if ClearMem then
 select case shr(VGA_3CF(6*VGA_MODE_COUNT+mode) and 0xC,2) 
   case 0: MemBase=0xA000 : MemRange=128*1024 //EGA/VGA
   case 1: MemBase=0xA000 : MemRange=64*1024  //EGA/VGA
   case 2: MemBase=0xB000 : MemRange=32*1024  //MDA
   case 3: MemBase=0xB800 : MemRange=32*1024  //CGA
 end select
 w=shl(atr,8) : mem.Pos=shl(MemBase,4) : for i=0 to MemRange/2-1 : mem.Word=w : next
end

?? "[INT 10] Finish set video mode AL=0x";Hex(cpu.AL,2)

//success
exit(true) : end
