Author Topic: Calling all Galleons from interplanetary (hyper) space  (Read 4632 times)

0 Members and 1 Guest are viewing this topic.

Offline Richard

  • Seasoned Forum Regular
  • Posts: 364
    • View Profile
Calling all Galleons from interplanetary (hyper) space
« on: November 16, 2020, 01:09:00 pm »
Topic title reminds me of a very old radio song...

I do not know if Galleon "Creator of QB64 Fame" is involved with this forum but...


QB64 seems to be very limited when it comes to using CALL Interrupt (IntNum, Inregs, Outregs). I was hoping to have a bit of a "tutorial" and behind the scenes information on how to install more IntNum functionality. I suppose that because of Windows, everything has to be emulated. Although many may not wish to have this as new features, as with low level coding in general, for those wanting to achieve ultimate performance eg by way of execution time, code size and simply that it can be straight forward and works  - it would be an A+.

I will attempt to use DOS interrupt 16h AH = 05h Write to keyboard buffer with CH= scan code CL=character and if it can work in QB64 it would be a useful feature for me. If it cannot work at present with QB64, it would be helpful to have some insight on what would be involved and how-to accomplish this for use in QB64 on Windows 10 x64 for a custom modification of QB64.

Offline johnno56

  • Forum Resident
  • Posts: 1270
  • Live long and prosper.
    • View Profile
Re: Calling all Galleons from interplanetary (hyper) space
« Reply #1 on: November 16, 2020, 06:36:46 pm »
Richard,

If memory serves correctly; The song was called "Calling Occupants (of interplanetary craft)" written by the group Klaatu in 1976. A year later (1977), The Carpenters, did the more recognised cover version.

J
Logic is the beginning of wisdom.

Offline NOVARSEG

  • Forum Resident
  • Posts: 509
    • View Profile
Re: Calling all Galleons from interplanetary (hyper) space
« Reply #2 on: November 16, 2020, 08:20:18 pm »
Windows XP can run all the DOS interrupts which proves it can be done.
XP runs 16 bit programs in VM  virtual mode.   Intel calls it virtual mode.

Incredibly 16 bit code run in XP virtual mode allows access to the windows file system.

To get INT 16h to work would require finding the matching windows API. Not sure never worked with Windows API

From there the trick is to call the windows API in Qb64 and compile it. Dont know for sure.
« Last Edit: November 16, 2020, 08:28:21 pm by NOVARSEG »

Offline SMcNeill

  • QB64 Developer
  • Forum Resident
  • Posts: 3972
    • View Profile
    • Steve’s QB64 Archive Forum
Re: Calling all Galleons from interplanetary (hyper) space
« Reply #3 on: November 16, 2020, 08:35:33 pm »
You can also run XP Mode in newer versions of windows, to run 16-bit code. 

https://www.skyneel.com/run-xp-simultaneously-in-windows-7-and-8-by-xp-mode#:~:text=How%20to%20run%20XP%20in%20Windows%207%20and,XP%20Mode%20in%20your%20PC.%20More%20items...%20

Basically, you just load windows XP into a virtual machine, and run it there, but if you follow the link above, it'll cover Windows "official" way for newer systems.

What I'm curious about is WHY anyone would need CALL INTERRUPT?  Exactly what are they trying to accomplish which can't be done with the existing, modern toolset, which we have?  I can understand wanting to have it working, if it's to port old code into a modern system, but if one's goal is to write new programs, why would they ever resort to anything so outdated and obsolete?   
https://github.com/SteveMcNeill/Steve64 — A github collection of all things Steve!

Offline Richard

  • Seasoned Forum Regular
  • Posts: 364
    • View Profile
Re: Calling all Galleons from interplanetary (hyper) space
« Reply #4 on: November 16, 2020, 09:45:27 pm »
@ everyone above

Thanks for all your replies.

@ Steve - one of my many QB64 related projects involves investigating into "Pushing the limits" of what my present hardware can do - in particular to see if any speed improvements can be achieved. Though it would not really matter for say keyboard stuff, depending on the task at hand, say for instance very heavy Graphics Display stuff (live video streaming manipulation combined with computer graphics processing) any significant speed improvements via software alone may be worth the effort.

I have read that the BIOS related stuff when executed is SLOWER than the equivalent WINDOWS intercept code - I eventually hope to determine if this statement is true or false for my hardware.

My personal observation, based on a very limited sample set, is that ANYTHING that used "C" in the process of generating the .exe file usually ends up being slower to execute, possibly limited in features, larger .exe size - than the best hand written assembly code which is optimized for speed size etc. A very long time ago, I read somewhere that with all the tens of thousands of programs written using "C" there were only 6 programs where the programmers/developers were really happy with their product (of course everyone may feel different about this statement).

QB64 is a very fine product, I am curious and interested in making it go "faster" (there will be limits to what I can do in code - besides using MEM blocks and such, and critically reviewing how I write/rewrite my code - I still feel that it hopefully can run much faster.

Now although no programs I write now need faster speed, as it will take me a very long time to achieve speed improvements beyond what QB64 in typical useage provides - I am just (wishful thinking) preparing my self for when I really really need or want dramatic performance increases.

So in summary IF calling Interrupts ends up being (significantly) faster, for instance, it may be worth the trouble for me to persue this avenue as part of a toolset (used with all the QB64 tools).

Now have a lot of "homework" to do based on everyone's replies.

Offline Richard

  • Seasoned Forum Regular
  • Posts: 364
    • View Profile
Re: Calling all Galleons from interplanetary (hyper) space
« Reply #5 on: November 16, 2020, 10:31:03 pm »
@ Steve

Could you advise me re the XPmode stuff.

I went to the link and assumed the material would work on Windows 10 x64 and because of network errors (and not eye-balling the bandwidth used) I ended up with 3 copies of WindowsXPMode_en-us.exe (492,597,008 bytes each).

  [ You are not allowed to view this attachment ]  

Is there anything I can do to use above on my laptop?

I don't know if it is just me, but here in Australia using cellular data via Telstra is unreliable (keeps dropping out) and so have costed unnecessary 1 Gbyte cellular data because of two files being duplicated. Ouch!

Now for yet another QB64 project (but this one now very very important because money is involved) - having an always visible network analyser window for bandwidth being used  AND AUDIO feature (always on) when bandwidth is in the higher bracket (say >100 KBPS) so if large duplicate files are occurring I may be able to "kill" these.  Any thing in anyone's toolset for this???
« Last Edit: November 16, 2020, 10:35:04 pm by Richard »


Offline Richard

  • Seasoned Forum Regular
  • Posts: 364
    • View Profile
Re: Calling all Galleons from interplanetary (hyper) space
« Reply #7 on: November 17, 2020, 07:18:21 am »
@ Steve

Just in case that you MIGHT be interested...

I ran the following code which compares speed of simple mouse position routines using INTERRUPT and _SCREENXY.


Code: QB64: [Select]
  1. TYPE RegType
  2.     ax AS INTEGER 'IN... AX = &H0003 return position and button status
  3.     bx AS INTEGER 'OUT.. BX = mouse status (left/right button pressed)
  4.     cx AS INTEGER 'OUT.. CX = mouse column position (pixels)
  5.     dx AS INTEGER 'OUT.. DX = mouse row    position (pixels)
  6.     bp AS INTEGER
  7.     si AS INTEGER
  8.     di AS INTEGER
  9.     flags AS INTEGER
  10. DIM SHARED RegistersIN AS RegType
  11. DIM SHARED RegistersOUT AS RegType
  12. SCREEN _NEWIMAGE(3200, 1800, 32): _FULLSCREEN
  13.  
  14. RegistersIN.ax = &H0003 '  return MOUSE position (pixels) and button status
  15.  
  16. t# = TIMER
  17. FOR i& = 1 TO 1000000000
  18.     CALL INTERRUPT(&H33, RegistersIN, RegistersOUT)
  19.     c% = RegistersOUT.cx ' mouse column position
  20.     r% = RegistersOUT.dx ' mouse row    position
  21. NEXT: t# = TIMER - t#: PRINT t#: PRINT
  22.  
  23. t# = TIMER
  24. FOR i& = 1 TO 1000000000
  25.     c% = _SCREENX
  26.     r% = _SCREENY
  27. NEXT: t# = TIMER - t#: PRINT t#
  28.  
  29.  

The times are 101.8125 seconds for INTERRUPT method   and   160.164 seconds for the _SCREENXY method.

Although the program does not do anything useful - it simply illustrates speed differences. However, if you can write faster code to do the same thing as the _SCREENXY method - please advise.

Personally, I would consider the approx 30% speed increase using INTERRUPT to be worth the effort in "speed-critical" applications. Of course, different interrupt calls would probably have widely varying results.

FellippeHeitor

  • Guest
Re: Calling all Galleons from interplanetary (hyper) space
« Reply #8 on: November 17, 2020, 07:31:21 am »
You're mistaking _MOUSEX and _MOUSEY (mouse position) with _SCREENX and _SCREENY (position of the window on screen).

Offline Richard

  • Seasoned Forum Regular
  • Posts: 364
    • View Profile
Re: Calling all Galleons from interplanetary (hyper) space
« Reply #9 on: November 17, 2020, 07:46:50 am »
@ Fellippe

Thanks for reply.

Changed to _MOUSEXY instead - times are 102.695 seconds INTERRUPT method and 65.492 seconds for _MOUSEXY method.

So for this example QB64 _MOUSEXY is FASTER than INTERRUPT   

According to my reading of the WIKI, INTERRUPT (&H33,...) functions 00 to 03 are the ONLY INTERRUPTS QB64 supports - correct me if I am wrong please.

Offline SMcNeill

  • QB64 Developer
  • Forum Resident
  • Posts: 3972
    • View Profile
    • Steve’s QB64 Archive Forum
Re: Calling all Galleons from interplanetary (hyper) space
« Reply #10 on: November 17, 2020, 08:38:14 am »
According to my reading of the WIKI, INTERRUPT (&H33,...) functions 00 to 03 are the ONLY INTERRUPTS QB64 supports - correct me if I am wrong please.

I was looking in the source at this, and I have no frigging idea.  LOL!

Code: C++: [Select]
  1. void call_interruptx(int32 intno, void *inregs,void *outregs){
  2.     if (new_error) return;
  3.     static byte_element_struct *ele;
  4.     static uint16 *sp;
  5.     /* reference
  6.         TYPE RegTypeX
  7.         AX AS INTEGER
  8.         BX AS INTEGER
  9.         CX AS INTEGER
  10.         DX AS INTEGER
  11.         BP AS INTEGER
  12.         SI AS INTEGER
  13.         DI AS INTEGER
  14.         FLAGS AS INTEGER
  15.         DS AS INTEGER
  16.         ES AS INTEGER
  17.         END TYPE
  18.     */
  19.     //error checking
  20.     ele=(byte_element_struct*)outregs;
  21.     if (ele->length<20){error(5); return;}//Illegal function call
  22.     ele=(byte_element_struct*)inregs;
  23.     if (ele->length<20){error(5); return;}//Illegal function call
  24.     //load virtual registers
  25.     sp=(uint16*)(ele->offset);
  26.     cpu.ax=sp[0];
  27.     cpu.bx=sp[1];
  28.     cpu.cx=sp[2];
  29.     cpu.dx=sp[3];
  30.     cpu.bp=sp[4];
  31.     cpu.si=sp[5];
  32.     cpu.di=sp[6];
  33.     //note: flags ignored (revise)
  34.     cpu.ds=sp[8];
  35.     cpu.es=sp[9];
  36.     call_int(intno);
  37.     //save virtual registers
  38.     ele=(byte_element_struct*)outregs;
  39.     sp=(uint16*)(ele->offset);
  40.     sp[0]=cpu.ax;
  41.     sp[1]=cpu.bx;
  42.     sp[2]=cpu.cx;
  43.     sp[3]=cpu.dx;
  44.     sp[4]=cpu.bp;
  45.     sp[5]=cpu.si;
  46.     sp[6]=cpu.di;
  47.     //note: flags ignored (revise)
  48.     sp[8]=cpu.ds;
  49.     sp[9]=cpu.es;
  50.     return;
  51. }

As far as I can tell, your BAS code which you provided above, shouldn't even work!

    ele=(byte_element_struct*)outregs;
    if (ele->length<20){error(5); return;}//Illegal function call
    ele=(byte_element_struct*)inregs;
    if (ele->length<20){error(5); return;}//Illegal function call

This little segment of error checking code seems to be saying: If the type we pass has a length less than 20, toss and error and exit...

Your type above is only 16 bytes...

Since it's not tossing an error, like I'd expect it to, that just tells me I have no dang clue what the heck is going on.  Felippe or Luke will have to be one to answer what the heck we're emulating and what we're not with CALL INTERRUPT.  Apparently, I need more sleep and more coffee afterwards, to try and figure it out.  :P
https://github.com/SteveMcNeill/Steve64 — A github collection of all things Steve!

Offline luke

  • Administrator
  • Seasoned Forum Regular
  • Posts: 324
    • View Profile
Re: Calling all Galleons from interplanetary (hyper) space
« Reply #11 on: November 17, 2020, 09:33:03 am »
There's something surreal in talking about PC BIOS Interrupts in 2020; just let the damn things die.

Anyway, Steve has quoted the implementation of INTERRUPTX when he should have quoted INTERRUPT (the previous function in libqb), which has a check of the expected size.

In either case, both functions call the call_int() function:
Code: [Select]
         void call_int(int32 i){
           
            if (i==0x33){
               
                if (cpu.ax==0){
                    cpu.ax=0xFFFF;//mouse installed
                    cpu.bx=2;
                    return;
                }
               
                if (cpu.ax==1){sub__mouseshow(NULL,0); return;}
                if (cpu.ax==2){sub__mousehide(); return;}
                if (cpu.ax==3){
                    //return the current mouse status
                    //buttons
                   
                    int32 handle;
                    handle=mouse_message_queue_default;   
                    mouse_message_queue_struct *queue=(mouse_message_queue_struct*)list_get(mouse_message_queue_handles,handle);
                   
                    //buttons
                    cpu.bx=queue->queue[queue->last].buttons&1;
                    if (queue->queue[queue->last].buttons&4) cpu.bx+=2;
                   
                    //x,y offsets   
                    static float mx,my;
                   
                    //temp override current message index to the most recent event
                    static int32 current_mouse_message_backup;
                    current_mouse_message_backup=queue->current;
                    queue->current=queue->last;
                   
                    mx=func__mousex(0,0); my=func__mousey(0,0);
                   
                    //restore "current" message index
                    queue->current=current_mouse_message_backup;
                   
                    cpu.cx=mx; cpu.dx=my;
                    //double x-axis value for modes 1,7,13
                    if ((display_page->compatible_mode==1)||(display_page->compatible_mode==7)||(display_page->compatible_mode==13)) cpu.cx*=2;
                    if (display_page->text){
                        //note: a range from 0 to columns*8-1 is returned regardless of the number of actual pixels
                        cpu.cx=(mx-0.5)*8.0;
                        if (cpu.cx>=(display_page->width*8)) cpu.cx=(display_page->width*8)-1;
                        //note: a range from 0 to rows*8-1 is returned regardless of the number of actual pixels
                        //obselete line of code: cpu.dx=(((float)cpu.dx)/((float)(display_page->height*fontheight[display_page->font])))*((float)(display_page->height*8));//(mouse_y/height_in_pixels)*(rows*8)
                        cpu.dx=(my-0.5)*8.0;
                        if (cpu.dx>=(display_page->height*8)) cpu.dx=(display_page->height*8)-1;
                    }
                    return;
                }
               
                if (cpu.ax==7){//horizontal min/max
                    return;
                }
                if (cpu.ax==8){//vertical min/max
                    return;
                }
               
                //MessageBox2(NULL,"Unknown MOUSE Sub-function","Call Interrupt Error",MB_OK|MB_SYSTEMMODAL);
                //exit(cpu.ax);
               
                return;
            }
           
        }

Note in particular:
 - Only the mouse interrupt, 33h, is supported.
 - Fetching the position is ultimately a call to func__mousex(), which is what _MOUSEX is behind the scenes.

To preempt further questions, call_int is also used to handle the INT opcode in CALL ABSOLUTE. The full set of opcodes handled is easily found by examining the call_absolute() function in libqb.

To reiterate: there is no good reason to be using PC BIOS interrupts in 2020. They are not some speed panacea, nor are they somehow "closer to the hardware". Just let them die already.

Offline Cobalt

  • QB64 Developer
  • Forum Resident
  • Posts: 878
  • At 60 I become highly radioactive!
    • View Profile
Re: Calling all Galleons from interplanetary (hyper) space
« Reply #12 on: November 17, 2020, 11:46:28 am »
To reiterate: there is no good reason to be using PC BIOS interrupts in 2020. They are not some speed panacea, nor are they somehow "closer to the hardware". Just let them die already.

Heil, mein Führer!



Didn't think "modern" OSes even allowed true direct contact with BIOS interrupts. thought it was all slow as hell emulation anyway. Direct hardware manipulation will always be faster than going through the Führer to get things done. Not that you can anymore, So...

mute point?
Granted after becoming radioactive I only have a half-life!

Offline TempodiBasic

  • Forum Resident
  • Posts: 1792
    • View Profile
Re: Calling all Galleons from interplanetary (hyper) space
« Reply #13 on: November 17, 2020, 12:08:51 pm »
Hi
about CALL INTERRUPT, CALL INTERRUPTX and CALL ABSOLUTE
I can imagine that together PEEK POKE INP and OUT are a difficult and advanced part of DOS programming.
And while Windows has been a mask of DOS it was all ok.
Now facing the windows API for windows users and Linux API for linux universe users, DOS (not BIOS) interrupts are an unsupported system.
I liked much to make TSR programs in DOS days, but now in modern OS there are different and more suitable way to get some results like Driver, DLL and other kind of shared libraries.
So IMHO this is a cultural barrier to break trough. IMHO users with QB45 experience need a perspective of new OSes, in details how to get the results got with specifical interrupt. For example if I wanted to limit mouse range of movement, in DOS I can call int33 with the dedicated service number et voilà le jeux sont fait. And in Win64 bit? I dunno!

On the old [abandoned, outdated and now likely malicious qb64 dot net website - don’t go there] I posted my personal mouse interrupt library, but I find more suitable _MOUSEINPUT _MOUSEX _MOUSEY etc etc function of QB64.
Thanks to read
Programming isn't difficult, only it's  consuming time and coffee

Offline Richard

  • Seasoned Forum Regular
  • Posts: 364
    • View Profile
Re: Calling all Galleons from interplanetary (hyper) space
« Reply #14 on: November 17, 2020, 12:48:04 pm »
@ Cobalt   &   @ TempodiBasic      thanks for your interest in this topic.

@ Steve thanks for trying to sort/analyze  this matter

@ Luke  thanks for your in depth reply - so all the QB64 INTERRUPT stuff (almost next to nothing) is effectively a QB64 tool already used but with a different "wrapper".

Quote
To preempt further questions, call_int is also used to handle the INT opcode in CALL ABSOLUTE. The full set of opcodes handled is easily found by examining the call_absolute() function in libqb.

I was curious and so looked at   libqb.cpp   but could not find the full set of opcodes handled - by opcodes I assume you mean the Intel opcodes (e.g.     MOV DX,...     CLI,    OUT DX,...    STI,    RETF  etc)

I was thinking of (for a totally different project) going along the lines of the following code fragments:-

Code: QB64: [Select]
  1.  
  2. REDIM t%(1 to 1024)
  3. GOSUB SetUp
  4. ...
  5. DEF SEG = VARSEG(t%(1))
  6. ...
  7. ...
  8. SetUp:
  9. DATA B0,80      'MOV AL,80
  10. ...
  11. DATA CB           ' RETF
  12.  
  13. RESTORE SetUp:
  14. DEF SEG = VARSEG(t%(1))
  15. np% = VARPTR(t%(1))
  16. FOR n%= np% TO 1024
  17.     READ byte$
  18.     POKE n%, VAL("&H" + byte$)
  19.  
  20.  
   

I was hoping that the above framework would allow the injection of opcodes directly into my program. Is this approach also DOOMED?

R.I.P. QB64 INTERRUPT(X)     and possibly CALL ABSOLUTE