DOS "Window" in FreeBasic graphic mode

DOS specific questions.
Post Reply
angros47
Posts: 2321
Joined: Jun 21, 2005 19:04

DOS "Window" in FreeBasic graphic mode

Post by angros47 »

DOS is not a multitasking system, although it allows to have more than one process in memory: only the last one loaded is executed, the others are suspended. When another program is launched (for example with the command SHELL) the original program pauses, and resumes when the other program terminates. This is not new, it was true even with QB and with GWBasic, although at the time when DOS was made it was an important new feature (on earlier systems loading a program overwrote the previous one), and sometimes a good enough "surrogate" of task switching. Many DOS program of the time had an option to call a DOS shell, so the user who had to save a file could use it to find a suitable directory, move away or delete unused data, and so on.

Opening a DOS CLI in FreeBasic is easy (SHELL command with no arguments), but works only under text mode. If you are in graphic mode, the DOS command line is not visible (although it's still possible to type commands and have them executed). Under QB and GWBasic the DOS prompt was visible, although it usually broke all the screen layout.

A more elegant solution would be to have the DOS console inside a window displayed on the graphic screen (as it was done under Windows 3.1, for example, or in DesqView). Besides Windows and Desqview, there aren't many other software for DOS that offer a similar features, one is WinDos by Oscar Gonzalez. I looked at the code to do that, and it was pretty simple, so I ported it to FreeBasic.

Code: Select all

#include "dos/dpmi.bi"
#include "dos/go32.bi"
#include "dos/sys/farptr.bi"

dim shared as _go32_dpmi_registers regs_0x10
dim shared as _go32_dpmi_seginfo   info_0x10
dim shared as _go32_dpmi_seginfo   old_vector_0x10
dim shared as _go32_dpmi_seginfo old_handler_0x08, new_handler_0x08


dim shared as integer VideoRows = 24
dim shared as integer VideoCols = 80
dim shared as integer CursorX = 0
dim shared as integer CursorY = 0
dim shared as integer CursorIniRow = 6
dim shared as integer CursorEndRow = 7
dim shared as integer Attr = &h07

dim shared as integer ox, oy 'Position of the 'Window'

Dim shared DosColors(16) As integer = {&H000000, &H0000AA, &H00AA00, &H00AAAA, &HAA0000, &HAA00AA, &HAA5500, &HAAAAAA, &H555555, &H5555FF, &H55FF55, &H55FFFF, &HFF5555, &HFF55FF, &HFFFF55, &HFFFFFF}


declare function Int_0x10 cdecl(r as _go32_dpmi_registers ptr) as integer


function GetChar(x as integer, y as integer) as integer
   _farsetsel(_go32_info_block.selector_for_linear_memory)
   return _farnspeekw( _go32_info_block.linear_address_of_primary_screen + 2 * ( ( ( y ) * VideoCols ) + ( x ) )   )
end function

sub PutCh(x as integer, y as integer, c as integer)
   _farsetsel(_go32_info_block.selector_for_linear_memory)
   _farnspokew( _go32_info_block.linear_address_of_primary_screen + 2 * ( ( ( y ) * VideoCols ) + ( x ) )  , c )
end sub



sub DrawScreen
screenlock
	static t as ubyte

	for y as integer=0 to VideoRows
		for x as integer=0 to VideoCols-1
			dim vmem as integer=GetChar(x,y)
			dim as ubyte c,a
			c=vmem and 255: a= vmem shr 8

			line (ox+x*8,oy+y*8)-step(7,7),DosColors(a shr 4),bf
			draw string (ox+x*8,oy+y*8),chr(c),DosColors(a and 15)
		next
	next

	t+=1: if t>18 then t=0
	if t>9 then 'draw blinking cursor
		'dim as integer p = 2 * (RAM[&h450 + RAM[&h462]*2] + RAM[&h451 + RAM[&h462]*2] * RAM[&h44A])

		line (ox+CursorX*8,oy+CursorY*8+CursorIniRow) - step(7,CursorEndRow-CursorIniRow),DosColors(Attr),bf
	end if

screenunlock

end sub

sub Bios_Scroll (x as integer, y as integer, w as integer, h as integer, d as byte, a as ubyte)
   dim as integer i, px, py
   if d=0 then
     for py=y to h
       for px=x to w
         PutCh (px,py,a*256+32)
       next
     next
     exit sub
   end if

   for py as integer= y to h
	  dim as integer yy = py - 1
	  for px = x to w
		 PutCh( px, yy, GetChar( px, py ) )
	  next
   next

   for px=x to w
     PutCh (px,h-1,a*256+32)
   next
end sub

sub Bios_Putchar( c as integer )
   select case c
   
      case &h0a		' Line feed 
         CursorY+=1
      case &h0d		' Return 
         CursorX = 0
      case &h08	 	' Backspace 
         CursorX-=1
      case &h07		'Bell 
         exit sub
      case else
         PutCh( CursorX, CursorY, c+ Attr shl 8 )
         CursorX+=1
   end select

   if  CursorX >= VideoCols then
	  CursorX = 0
	  CursorY+=1
   end if
   if  CursorX < 0 then
	  CursorX = VideoCols - 1
	  CursorY-=1
   end if
   if  CursorY >= VideoRows then
	  Bios_Scroll( 0, 1, VideoCols - 1, VideoRows, 1, Attr )
	  CursorY = VideoRows - 1
   end if
   if  CursorY < 0 then
	  CursorY = 0
   end if
end sub




function Int_0x10(r as _go32_dpmi_registers ptr) as integer
   dim as integer i,j
   select case r->h.ah
   case &h00
     Attr = &h07
     select case r->h.al
     case 0,1
        VideoCols=40:VideoRows=24
     case 3
        VideoCols=80:VideoRows=24
     end select
   case &h01
     CursorIniRow=r->h.ch
     CursorEndRow=r->h.cl
   case &h02
     CursorX=r->h.dl
     CursorY=r->h.dh
   case &h03
     r->h.dl=CursorX
     r->h.dh=CursorX
   case &h06
     Attr = r->h.bh
     Bios_Scroll( r->h.cl, r->h.ch, r->h.dl, r->h.dh, r->h.al, Attr )
   case &h07
     Attr = r->h.bh
     Bios_Scroll( r->h.cl, r->h.ch, r->h.dl, r->h.dh, -r->h.al, Attr )
   case &h8
     i=GetChar( CursorX, CursorY )
     r->h.al=i and &hFF
     r->h.ah=i shr 8
   case &h9
     Attr=r->h.bl
     for i = 1 to r->x.cx
	Bios_Putchar( r->h.al )
     next
   case &h0A
     for i = 1 to r->x.cx
	Bios_Putchar( r->h.al )
     next
   case &h0E
     Bios_Putchar( r->h.al )
   case &hF
     r->h.al=&h03
     r->h.ah=VideoCols
   case &h11
     select case r->h.al
     case &h2, &h12
        if VideoRows = 24 then VideoRows = 50 else VideoRows = 24

     case &h30
        r->h.cl = &h10
        r->h.ch = &h10
	r->h.dl = &h18
	r->x.es = &hC000
	r->x.bp = &h422C
	r->x.cx = &h10

     end select
   case &h12
     select case r->h.bl
     case &h10
        r->h.bl = &h0
        r->h.bh = &h0
	r->x.cx = &h0

     end select
   case &h12
        r->h.bl = 8
   end select



   return 1
end function

private sub Int_0x10_end cdecl()
end sub

sub Int_0x08 cdecl()
  static D as ubyte
  if d=0 then
    d=255
    DrawScreen
    d=4
  elseif d<255 then
    d-=1
  end if
end sub
private sub Int_0x08_end cdecl()
end sub



sub InstallInt0x10
'   LOCK_FUNCTION( My_Int_0x10 );
   dim as byte ptr ptr_end = cast( byte ptr, @Int_0x10_end )
   dim as byte ptr ptr_start = cast( byte ptr, @Int_0x10 )
   _go32_dpmi_lock_code(@Int_0x10, ptr_end-ptr_start)

'   memset( VideoChar, 0x20, 80 * 25 );
'   memset( VideoAttr, Attr, 80 * 25 );
'   Cls( &Me );
    
   for y as integer=0 to VideoRows
     for x as integer=1 to VideoCols
       PutCh (x,y,attr*256+32)
     next
   next

   _go32_dpmi_get_real_mode_interrupt_vector( &h10, @old_vector_0x10 )
   info_0x10.pm_offset = cast (integer,@Int_0x10)
   _go32_dpmi_allocate_real_mode_callback_iret( @info_0x10, @regs_0x10 )
   _go32_dpmi_set_real_mode_interrupt_vector( &h10, @info_0x10 )
end sub

sub RemoveInt0x10()
   _go32_dpmi_set_real_mode_interrupt_vector( &h10, @old_vector_0x10 )
   _go32_dpmi_free_real_mode_callback( @info_0x10 )
end sub


sub InstallInt0x08()
   dim as byte ptr ptr_end = cast( byte ptr, @Int_0x08_end )
   dim as byte ptr ptr_start = cast( byte ptr, @Int_0x08 )
   _go32_dpmi_lock_code(@Int_0x08, ptr_end-ptr_start)

   _go32_dpmi_get_protected_mode_interrupt_vector( &h08, @old_handler_0x08 )

   new_handler_0x08.pm_offset = cast(integer,@Int_0x08)
   new_handler_0x08.pm_selector = _go32_my_cs()
   _go32_dpmi_chain_protected_mode_interrupt_vector( &h08, @new_handler_0x08 )
end sub

sub RemoveInt0x08()
   _go32_dpmi_set_protected_mode_interrupt_vector( &h08, @old_handler_0x08 )
end sub




screenres 800,600,32
line (0,0)-(800,600),rgb(0,0,255),bf

ox=80:oy=200


InstallInt0x10
InstallInt0x08
shell
RemoveInt0x10
RemoveInt0x08
(Note: WinDos was released under GPL, so I guess this code is supposed to be under GPL too)
exagonx
Posts: 314
Joined: Mar 20, 2009 17:03
Location: Italy
Contact:

Re: DOS "Window" in FreeBasic graphic mode

Post by exagonx »

angros47 wrote:DOS is not a multitasking system,
But you can manage more than one proces with thread istruction.
angros47
Posts: 2321
Joined: Jun 21, 2005 19:04

Re: DOS "Window" in FreeBasic graphic mode

Post by angros47 »

No. Thread instructions allow you to manage more than one thread, not more than one process. Processes and threads are not the same thing.
exagonx
Posts: 314
Joined: Mar 20, 2009 17:03
Location: Italy
Contact:

Re: DOS "Window" in FreeBasic graphic mode

Post by exagonx »

angros47 wrote:No. Thread instructions allow you to manage more than one thread, not more than one process. Processes and threads are not the same thing.
Yes you are right but is useless because freebasic ave no header for multi process or multi thread in dos version, time ago my friend writed a library in DJCCP for msdos and I used it for make a program wiht 3 thread where 2 thread was 2 different file batch in loop, and its worked.
marcov
Posts: 3455
Joined: Jun 16, 2005 9:45
Location: Netherlands
Contact:

Re: DOS "Window" in FreeBasic graphic mode

Post by marcov »

Also, most dos thread libs only exploit one core, since Dos is not a multiprocessing OS either. IOW the threads don't run at the same time, but interleaved in short intervals
angros47
Posts: 2321
Joined: Jun 21, 2005 19:04

Re: DOS "Window" in FreeBasic graphic mode

Post by angros47 »

Multithread in DOS version of FreeBasic is officially supported since version 1.06.

The patch to use threads in DOS was developed in 2013 by an user named monochromator, who has then left and stopped working on it.
In 2017, the patch was ported to the most recent version of FreeBasic, and integrated in the official build by me.

Multithread programs in DOS don't work under DosBox, but they work under the windows command prompt, or under DOSEmu in Linux. They also should work on pure DOS. They are based on an old port of pthread that has not been maintained in ages, and I guess that's the best that can be done for multithreading in DOS, nowadays. It is based on signals, and it seems DosBox doesn't emulate them correctly.

By combining multithreading with dynamic libraries it should be possible to load external pieces of code and run them concurrently. It is not possible, instead, to use threads to run different DOS programs (if you run a SHELL command, all threads on current program are paused, not just the one calling the SHELL command)

The closest thing to a true multitasking I have managed to achieve is this one: https://freebasic.net/forum/viewtopic.php?f=4&t=28490. It uses interrupts and coroutines (coroutines are still another way to achieve a sort of multitasking, more limited than threads or processes). The solution I used in that example is basically a dirty use of interrupts to activate a coroutine. Unstable as hell, but it seems to work (don't try running anything serious on it)

@marcov
The thread solution implemented in DOS FreeBasic uses signals to pause execution and switch from a thread to another, so you are right. Still, if someone is using DOS as main operating system, I seriously doubt they are using a multicore processor anyway.
angros47
Posts: 2321
Joined: Jun 21, 2005 19:04

Re: DOS "Window" in FreeBasic graphic mode

Post by angros47 »

Looks like the Int_0x10 should have these lines added:

Code: Select all

   case else
        _go32_dpmi_set_real_mode_interrupt_vector(&h10, @old_vector_0x10)
        _go32_dpmi_simulate_int(&h10,r)
        _go32_dpmi_set_real_mode_interrupt_vector(&h10, @info_0x10)
Post Reply