'*****************************************************************************
'* INT 10H AH=00h (CGA 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)

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
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

//video mode to set
mode=cpu.AL

//write CRT register values from table at vector 0x1D
select case mode
  case 0x0 to 0x1 : b=0 //TEXT 40x25
  case 0x2 to 0x3 : b=1 //TEXT 80x25
  case 0x4 to 0xA : b=2 //GRAPH
  case else : exit(false) : end select
textmode=b<2
offs=mem.Word(0x1D*4) : seg=mem.Word(0x1D*4+2) : mem.Pos=shl(seg,4)+offs+16*b
for i=0 to 15 : pc.WritePort(0x3D4,i) : pc.WritePort(0x3D5,mem.Byte) : next

//get other params of new mode
select case mode
  case 0 : 	//TEXT 40x25 (HALFTONE 16)
   mode_port=0 or shl(1,2) or shl(1,3) : color_port=0 
   col_count=40 : row_count=25 : buf_size=col_count*row_count*2
  case 1 :	//TEXT 40x25 (COLOR 16)
   mode_port=0 or shl(1,3) : color_port=0 
   col_count=40 : row_count=25 : buf_size=col_count*row_count*2
  case 2 : 	//TEXT 80x25 (HALFTONE 16)
   mode_port=1 or shl(1,2) or shl(1,3) : color_port=0 
   col_count=80 : row_count=25 : buf_size=col_count*row_count*2
  case 3 : 	//TEXT 80x25 (COLOR 16)
   mode_port=1 or shl(1,3) : color_port=0 
   col_count=80 : row_count=25 : buf_size=col_count*row_count*2
  case 4 : 	//GRAPH 320x200 (COLOR 4)
   mode_port=0 or shl(1,1) or shl(1,3) : color_port=0x20
   col_count=40 : row_count=25 : buf_size=(col_count*row_count*8*8)/4
  case 5 :	//GRAPH 320x200 (HALFTONE 4)
   mode_port=0 or shl(1,1) or shl(1,3) or shl(1,2) : color_port=0x20
   col_count=40 : row_count=25 : buf_size=(col_count*row_count*8*8)/4
  case 6 : 	//GRAPH 640x200 (HALFTONE 2)
   mode_port=0 or shl(1,1) or shl(1,3) or shl(1,4) : color_port=0
   col_count=80 : row_count=25 : buf_size=(col_count*row_count*8*8)/8
  case 7 :	//GRAPH 160x200 (COLOR 16 COMPOSITE)
   mode_port=0 or shl(1,1) or shl(1,3) or shl(1,4) : color_port=0
   col_count=20 : row_count=25 : buf_size=(col_count*row_count*8*8)/2
  case 8 : 	//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
  case 9 : 	//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
  case 0xA : 	//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 
   buf_size=(col_count*row_count*8*8)/4
end select

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

//write params to CRT
pc.WritePort(0x3D9,color_port) : pc.WritePort(0x3D8,mode_port)
pc.WritePort(0x3DB,0) 

//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

//clear video buffer (and fill with attribute for text mode)
if textmode then w=0x0700 else w=0 : mem.Pos=0xB8000
for i=0 to buf_size/2-1 : mem.Word=w : next

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

//success
exit(true) : end
