News:

Masm32 SDK description, downloads and other helpful links
Message to All Guests
NB: Posting URL's See here: Posted URL Change

Main Menu

Double buffer in mode 13H

Started by popcalent, February 07, 2024, 04:46:28 PM

Previous topic - Next topic

NoCforMe

OK, another approach: instead of hooking any interrupts, just poll the keyboard in a tight loop looking for your keystrokes:
QuoteINT 16h, AH = 01

Returns:
    ZF = 0 if a key pressed (even Ctrl-Break)
    AX = 0 if no scan code is available
    AH = scan code
    AL = ASCII character or zero if special function key

Tight loop:
tloop: MOV AH, 1
INT 16H
TEST AX, AX
JZ tloop

; Key code/char. is in AH/AL:

(This one doesn't wait for a keystroke but returns immediately.)

BTW, forget about hooking INT 9, as keycodes are nowhere to be found (the ISR places the latest keystroke in a circular buffer).
Assembly language programming should be fun. That's why I do it.

sinsi

Quote from: _japheth on February 28, 2024, 02:53:23 PMHello,

it's an interesting "race condition" problem. You're writing to variable PRESSED_KEY in both your main loop and your interrupt procedure. That cannot work without some "synchronization".
If I remember the BIOS code, the first instruction is STI, then a read from port 60, then an INT 15 call, so an overwrite is possible.
QuoteThe best and simplest approach of course is NOT to write to that variable in your main loop.
🍺🍺🍺

popcalent

Quote from: sinsi on February 28, 2024, 02:41:04 PMWhich emulator? Some of them are known for timing issues which can affect keyboard input.
The FASM code I have uses a 128-byte key array, not just a single byte, but when I converted your code it exited as soon as ESC was pressed.
I'm using dosbox. I have a 386 PC104 board here that I want to assemble with some parts to have an MSDOS working computer, but I need to find the time to do so.

popcalent

Quote from: _japheth on February 28, 2024, 02:53:23 PMHello,

it's an interesting "race condition" problem. You're writing to variable PRESSED_KEY in both your main loop and your interrupt procedure. That cannot work without some "synchronization". The best and simplest approach of course is NOT to write to that variable in your main loop.


Without the variable write in the main loop, it seems to work (I just tried it a couple of times). However, as of now, the program only checks if ESC has been pressed, and if so, it exits. The aim is to evaluate the cursor keys. The motivation to write the variable with a zero in the main loop is that the next iteration of the loop does not evaluate the same value of the variable, even when no new key has been pressed. Without that write, the loop would evaluate a scan code, a release code, and every subsequent iteration of the loop would evaluate that same release code until a new key is pressed.

popcalent

Quote from: NoCforMe on February 28, 2024, 03:16:16 PMOK, another approach: instead of hooking any interrupts, just poll the keyboard in a tight loop looking for your keystrokes:
tloop: MOV AH, 1
INT 16H
TEST AX, AX
JZ tloop

; Key code/char. is in AH/AL:

Iirc, this was my first approach. However the problem is that when you press a cursor key, the sprite moves towards the direction of the cursor key, stalls, then it moves without stalling. If you press another cursor key while another one is pressed, then the sprite stalls.

If you don't understand what I mean by "moves, stalls, moves without stalling", try pressing a letter key without releasing anywhere where you can enter text (I just tried here while writing this comment). You'll see the letter appears once, then it stalls, then a bunch of that letter appear without interruption.

_japheth

Quote from: popcalent on February 28, 2024, 03:56:40 PMWithout the variable write in the main loop, it seems to work (I just tried it a couple of times). However, as of now, the program only checks if ESC has been pressed, and if so, it exits. The aim is to evaluate the cursor keys. The motivation to write the variable with a zero in the main loop is that the next iteration of the loop does not evaluate the same value of the variable, even when no new key has been pressed. Without that write, the loop would evaluate a scan code, a release code, and every subsequent iteration of the loop would evaluate that same release code until a new key is pressed.

For gaming, the best approach - as sinsi already has mentioned - is to maintain an array of 128 bits/bytes that tell you the status of all keys at any time

Here's an example how you could implement that in your int 15h interrupt proc, using 80386 instructions:

push ax
mov ah, 0
btr ax,7          ; pressed or released?
jc released
bts cs:[keybittab], ax
jmp done
released:
btr cs:[keybittab], ax
done:
pop ax

with the bit array defined as:
keybittab dw 8 dup (?)

And then, if you want to know the current status of the ESC key, just code

bt cs:[keybittab], 1
jc is_pressed

Dummheit, gepaart mit Dreistigkeit - eine furchtbare Macht.

NoCforMe

I want to back up a bit here and go back over some ground again. Hopefully this won't add to the confusion here.

Thing is, I may have misunderstood what the OP is trying to do here (and others may have as well).

This all concerns keyboard handling for his game program. I'm now wondering if using any of the BIOS functions is the wrong way to go here. Why? Because apparently the OP needs to handle the case where one key is pressed and then another key is pressed without releasing the first key. I believe this falls under the heading of key "rollover", and I don't think the BIOS handles this correctly for his application. (I could be wrong here.)

So let me ask @popcalent: is this the case? Do you need to be able to handle a new keypress with another key already held down?

Another reason not to use the BIOS is that it automatically goes into "typematic" mode when a key is held down, generating multiple keypresses from that key. I wonder if there's some way to disable this behavior when the game program is running.

If it's true that this kind of keyboard handling is required, then do the DOS experts here know whether this case can be handled correctly using BIOS functions (like INT 15h/AH = 4Fh)?

If not, then how about this: go to the lowest-level interface and access the keyboard through port 60 (and 64) to get the immediate status. Here's a page describing that interface.
Assembly language programming should be fun. That's why I do it.

popcalent

Quote from: NoCforMe on February 29, 2024, 11:54:02 AMSo let me ask @popcalent: is this the case? Do you need to be able to handle a new keypress with another key already held down?
Yes. This is the case. I was almost able to achieve this using a buffer as described here:
Quote from: popcalent on February 12, 2024, 09:06:00 AM.
, but the problem was that sometimes the program did not register key releases and the sprite kept moving.

sinsi

QuoteAnother reason not to use the BIOS is that it automatically goes into "typematic" mode when a key is held down, generating multiple keypresses from that key
Nope, it's the controller in hardware. That's one of the many reasons to use low-level BIOS routines.

If th OP wants to handle more than one key, a 128-slot buffer is really the only way.
🍺🍺🍺

NoCforMe

#84
Well, that might be true.

I'm getting curious enough about this issue to maybe try some coding. Problem is that I don't have any kind of DOS emulator. Is there something I can install on my Windows 7 computah that will properly emulate DOS? DosBox? vDos?

What I'm thinking of doing is coding a key-logging program to just spit out all keystroke events to a file. Should be low enough latency to capture all keyboard events. That might be able to prove a method that will work for the OP.
Assembly language programming should be fun. That's why I do it.

FORTRANS

Hi,

Quote from: NoCforMe on March 01, 2024, 07:00:50 AMWell, that might be true.

I'm getting curious enough about this issue to maybe try some coding. Problem is that I don't have any kind of DOS emulator. Is there something I can install on my Windows 7 computah that will properly emulate DOS? DosBox? vDos?

   I am using Oracle's VirtualBox with Windows 8.1.  Not exactly DOS but
a version of OS/2 that runs DOS programs.  Seems to work here.

   Or, if your system allows, just boot up DOS from a bootable media.
That has worked with older computers for me.

Regards,

Steve N.

daydreamer

Quote from: FORTRANS on March 01, 2024, 11:49:44 PMI am using Oracle's VirtualBox with Windows 8.1.  Not exactly DOS but
a version of OS/2 that runs DOS programs.  Seems to work here.

   Or, if your system allows, just boot up DOS from a bootable media.
That has worked with older computers for me.
I am using dosbox an switch to energy setting that stops turbo increase clock freqency,to stop it from change speed

But even when i had my old 300 mhz p2 cpu,dont you return to same old problem old dos games run superfast if you boot dos ?
Now my slowest is 2 ghz cpu
OP intention seem to run it on old 386
my none asm creations
https://masm32.com/board/index.php?topic=6937.msg74303#msg74303
I am an Invoker
"An Invoker is a mage who specializes in the manipulation of raw and elemental energies."
Like SIMD coding

NoCforMe

OK, here's something to maybe chew on: I finally got a test program working in DOSBox. It's a key-logging program that uses INT 15h/AH=4Fh to capture keycodes to a buffer, then to write them to a log file after ESC is pressed (or the buffer is full).

Interesting, to say the least: first of all, I'm not sure how much of the behavior of this app is due to just the weirdness of running DOSBox on a (fairly) modren computah (Intel Core 2 Duo). Anyhow, here's what I've seen:

You get (at least) 2 keycodes per key, one for pressing, the other for releasing. If you just hit ESC right away you get this:

Key: 009C
Key: 0001

(keycodes in hex, obviously)

OK, fine. But look what happens if I run the logger, hit the right arrow key ONCE, then ESC:

Key: 009C
Key: 00E0
Key: 004D
Key: 00E0
Key: 00CD
Key: 0001


The cursor keys generate 2 keycodes each for press & release. But what's weird is that the ESC press keycode is first in the buffer, even though it was pressed AFTER the arrow key, while the release code is at the end as expected.

Have I created a time machine here? I've been over and over the code, and I'm 98% sure it's correct. (Source & executable attached here.)

Anyhow, play around with this and see what you think. Maybe someone else can figure this out. You can try testing key rollover with this (hold down one key and press another).

About the source: fairly straightforward 16-bit code. The complicated stuff at the end is my sprintf() function 'cause I like to see formatted output; you can just ignore it (or use it if you like).
Assembly language programming should be fun. That's why I do it.

sinsi

The controller needs some way of distinguishing between two keys that are the same (e.g. ctrl).
That's what the E0 is, a prefix byte.
In your case the keyboard sent the make code for the keypad right arrow (E0 4D) then the break code (E0 CD). If you are totally bypassing the BIOS, see what byte sequence the pause key sends.

Key: 009C esc released   edit: WRONG it's the enter key
Key: 00E0 prefix
Key: 004D right pressed
Key: 00E0 prefix
Key: 00CD right released
Key: 0001 esc presewed
🍺🍺🍺

NoCforMe

OK, I get the prefix codes; that's what I expected.

What I can't wrap my head around is the sequence of these codes. First of all, you say that the first code (9C) is the ESC release code; are you sure? Shouldn't that be the make code? How could the release code come before the make code?

You looked at my source, yes? I'm buffering the keycodes in the order they occur from INT 15h/AH=4Fh, and I'm writing them to the log file in the same order. So how come they're ... out of order?
Assembly language programming should be fun. That's why I do it.