Создание игры в понг на ассемблере, как мне получить ввод нескольких клавиш одновременно?

Я новичок, поэтому этот код, вероятно, не годится, я использовал для этого int 16h, но я мало знаю об этом int. Я только что узнал, что нельзя одновременно нажимать несколько клавиш; чем помочь?
Проблема с этим кодом в том, что одновременно может двигаться только одна доска, а мне нужны обе. Как проверить наличие нескольких входов?

Вот код для тех, кто хочет:

IDEAL
MODEL small
STACK 100h
DATASEG
; --------------------------
; Your variables here
; --------------------------
    line1X dw 80
  line1Y dw 120
  line1start dw 5
  line1end dw 10
  line2X dw 80
  line2Y dw 120
  line2start dw 310
  line2end dw 315
CODESEG
    proc startVideo ;creates video mode
      mov al,13h
      mov ah,0h
      int 10h
      ret
  endp startVideo
  proc clearScrean ;turns the screen black
      mov ah, 0ch
      xor al,al
      mov dx,200 
BlankLine:
      mov cx,320
BlankColumn:
        int 10h
        Loop BlankColumn
      dec dx
        cmp dx,0
        jne BlankLine     
      ret
  endp clearScrean
  
    proc drawboard ;creates the board
      push bp
      mov bp,sp
      mov al,0fh
      mov ah,0ch
      beginning equ [bp+10]
      fn equ [bp+8]
      X equ [bp+6] ;boards start
      Y equ [bp+4] ;boards end
      mov dx,Y
drawrow:
      mov cx,fn
drawcolumn:
        int 10h
      dec cx
        cmp cx,beginning
        jne drawcolumn
      dec dx
      cmp dx,X 
      jne drawrow
      pop bp
      ret 8
  endp drawboard
  proc drawall
      push [line1start]
      push [line1end]
      push [line1X]
      push [line1Y]
      call drawboard
      push [line2start]
      push [line2end]
      push [line2X]
      push [line2Y]
      call drawboard
      ret
  endp drawall
  proc boardup
      push bp
      mov bp,sp
      mov bx,[bp+4]
      mov si,[bp+6]
      cmp [word ptr bx],0 ;checks if board didnt get to border 
      je fn1
        call clearScrean
      sub [word ptr bx],5 ;3 pixels added to board
      sub [word ptr si],5
      call drawall ;prints both boards
fn1:
      pop bp
      ret 4
  endp boardup
  proc boarddown
      push bp
      mov bp,sp
      mov bx,[bp+4]
      mov si,[bp+6]
      cmp [word ptr si],200 ;checks if board didnt get to border 
      je fn2
        call clearScrean
      add [word ptr bx],5 ;3 pixels added to board
      add [word ptr si],5
      call drawall ;prints both boards
fn2:
      pop bp
      ret 4
  endp boarddown
start:
  mov ax, @data
  mov ds, ax
    mov bh,0
  call startVideo
  call clearScrean
  call drawall
checkskey: ;checks if key is being pressed
  mov ah,1
  int 16h
  jz checkskey ;jumps if key isnt pressed
  mov ah,0 ;checks which key is pressed
  int 16h
  cmp ah,11h ;if key pressed is w jump to upboard
  je upboard1
  cmp ah,01fh ;if key pressed is s jump to downboard
  je downboard1
  cmp ah,050h
  je downboard2
  cmp ah,048h
  je upboard2
  jmp checkskey ;if key isnt pressed jump to check key
upboard1: ;board 1 goes up
    push offset line1Y
  push offset line1X
  call boardup
  jmp checkskey
downboard1: ;board 1 goes down
    push offset line1Y
  push offset line1X
  call boarddown
  jmp checkskey
downboard2:
    push offset line2Y
  push offset line2X
  call boarddown
  jmp checkskey
upboard2:
    push offset line2Y
  push offset line2X
  call boardup
  jmp checkskey
exit:
  mov ax, 4c00h
  int 21h
END start

person orraz1    schedule 04.05.2021    source источник
comment
за этот вопрос нужно проголосовать тысячу раз только за идею написать игру на ассемблере...   -  person monty    schedule 04.05.2021
comment
школьный проект :P в любом случае делать понг на сборке, честно говоря, очень весело   -  person orraz1    schedule 04.05.2021
comment
Ответит ли получить два ключа одновременно - сборка 8086 на ваш вопрос? Я предполагаю, что это будет.   -  person U880D    schedule 05.05.2021
comment
Может быть, честно, я попробую изучить порты   -  person orraz1    schedule 05.05.2021


Ответы (2)


Другой ответ касается многопользовательской игры, в которой ни один из игроков не нажимает свои выделенные клавиши и, таким образом, не загружает клавиатуру. Хотя этот сценарий вполне разумен, вы можете разрешить игрокам удерживать клавишу нажатой в течение более длительного периода времени. Для этого мы можем заменить обработчик клавиатуры, предоставляемый BIOS/DOS, нашим собственным обработчиком.

С каждой клавишей на клавиатуре связано уникальное 8-битное число, которое мы называем скан-кодом.
При каждом нажатии клавиши клавиатура делает скан-код соответствующей клавиши доступным на порту 60h. Клавиатура также генерирует прерывание 09h. Обработчик этого прерывания может проверить скан-код и обработать его любым способом. Это то, что делает приведенная ниже демонстрационная программа.
Когда клавиша нажата, скан-код представляет собой байт с отключенным старшим битом. Когда клавиша отпущена, скан-код представляет собой байт со старшим битом. Остальные 7 бит остаются одинаковыми как для нажатия, так и для отпускания.

Следует отметить, что, хотя он подходит для вашей игры в понг, встроенный обработчик подстановки является минималистичным. Сложный обработчик также будет учитывать расширенные скан-коды с префиксом E0h или E1h.

Программа имеет дополнительные комментарии, чтобы вы могли легко понять, что происходит. Код использует синтаксис FASM. Демонстрация нормально работает в реальной среде DOS и в DOSBox (0.74).

; Multi-player Keyboard Input (c) 2021 Sep Roland

    ORG  256               ; Output will be a .COM program

    mov  ax, 3509h         ; DOS.GetInterruptVector
    int  21h               ; -> ES:BX
    push es bx             ; (1)

    mov  dx, Int09
    mov  ax, 2509h         ; DOS.SetInterruptVector
    int  21h

    mov  ax, 0013h         ; BIOS.SetVideoMode 320x200 (256 colors)
    int  10h
    mov  ax, 0A000h        ; Video buffer
    mov  es, ax
    cld                    ; So we can use the string primitive STOSB

Cont:
    mov  si, 160           ; Width
    mov  di, 100           ; Height

    mov  al, 0             ; Black
    cmp  [KeyList+48h], al ; Up
    je   .a
    mov  al, 2             ; Green
.a: mov  cx, 160           ; X
    mov  dx, 0             ; Y
    call Paint

    mov  al, 0             ; Black
    cmp  [KeyList+50h], al ; Down
    je   .b
    mov  al, 14            ; Yellow
.b: mov  cx, 160           ; X
    mov  dx, 100           ; Y
    call Paint

    mov  al, 0             ; Black
    cmp  [KeyList+11h], al ; aZerty / qWerty
    je   .c
    mov  al, 4             ; Red
.c: mov  cx, 0             ; X
    mov  dx, 0             ; Y
    call Paint

    mov  al, 0             ; Black
    cmp  [KeyList+1Fh], al ; S
    je   .d
    mov  al, 1             ; Blue
.d: mov  cx, 0             ; X
    mov  dx, 100           ; Y
    call Paint

    cmp  byte [KeyList+1], 0 ; ESC
    je   Cont

    pop  dx ds             ; (1)
    mov  ax, 2509h         ; DOS.SetInterruptVector
    int  21h

    mov  ax, 4C00h         ; DOS.Terminate
    int  21h
; --------------------------------------
Int09:
    push ax bx
    in   al, 60h
    mov  ah, 0
    mov  bx, ax
    and  bx, 127           ; 7-bit scancode goes to BX
    shl  ax, 1             ; 1-bit press/release goes to AH
    xor  ah, 1             ; -> AH=1 Press, AH=0 Release
    mov  [cs:KeyList+bx], ah
    mov  al, 20h           ; The non specific EOI (End Of Interrupt)
    out  20h, al
    pop  bx ax
    iret
; --------------------------------------
; IN (al,cx,dx,si,di)
Paint:
    push cx dx di          ; AL=Color CX=X DX=Y SI=Width DI=Height
    push ax                ; (1)
    mov  ax, 320           ; BytesPerScanline
    mul  dx
    add  ax, cx            ; (Y * BPS) + X
    mov  dx, di
    mov  di, ax
    pop  ax                ; (1)
.a: mov  cx, si
    rep  stosb
    sub  di, si
    add  di, 320
    dec  dx
    jnz  .a
    pop  di dx cx
    ret
; --------------------------------------
KeyList db 128 dup 0
KeyList db 128 dup 0

KeyList программы записывает текущее состояние всех клавиш на клавиатуре. Если байт равен 0, клавиша не нажата. Если байт равен 1, эта клавиша в данный момент нажата.

person Sep Roland    schedule 07.05.2021
comment
это безумие, спасибо, я попробую перевести этот код в tasm и поместить его в свой проект, tysm. - person orraz1; 16.05.2021
comment
Извините за вопросы, но что такое DOS.GetInterruptVector и DOS.GetInterruptVector? И что именно вы делаете в этом коде? - person orraz1; 08.06.2021
comment
Nvm теперь я понимаю omfg это гениально - person orraz1; 08.06.2021
comment
у вас есть статьи о том, как использовать прерывание 21h 25th? Я знаю, как это работает, но не знаю, как вы его используете. Кстати, где я могу поместить код прерывания реализации? - person orraz1; 08.06.2021

проблема с этим кодом в том, что одновременно может двигаться только одна доска, а мне нужны обе

Ощущение одновременности возникает из-за того, что ты действуешь быстро, очень быстро. Почти все в вашем компьютере работает последовательно, но мы воспринимаем многие вещи как происходящие параллельно.

Ваш код checkskey в порядке. На одной доске используются клавиши q и s, а на другой — клавиши вверх и вниз.
Как только клавиша становится доступной, функция BIOS клавиатуры 00h немедленно извлечет ее, и ваша программа соответствующим образом обновит графику. Но если ваши процедуры графического вывода занимают слишком много времени, то игроки начнут думать, что клавиатура тормозит.

Глядя на ваши графические подпрограммы, я вижу, что вы используете функцию Video BIOS 0Ch для размещения пикселей на экране. Это медленно и особенно болезненно, так как вы играете на самом простом графическом экране, где вы можете всего MOV байта, чтобы нарисовать пиксель.

В программе, которая нуждается в быстрой графике, может быть очень выгодно, чтобы регистр сегмента ES постоянно указывал на видеобуфер.

  mov  ax, 0A000h
  mov  es, ax
  cld             ; Because of the use of STOSB

Это все, что нужно, чтобы очистить экран:

ClearScreen:
  xor  di, di
  mov  cx, 64000
  mov  al, 0
  rep stosb
  ret

Вот как вы рисуете горизонтальную линию (160 100)-(200 100):

  mov  dl, 15    ; BrightWhite
  mov  cx, 51    ; 51 pixels from 160 to 200
  mov  bx, 160   ; X
  mov  ax, 100   ; Y
  call DrawLine

  ...

DrawLine:
  push dx
  mov  di, 320   ; BytesPerScanline
  mul  di
  add  ax, bx
  mov  di, ax    ; Address DI = (Y * BPS) + X
  pop  ax        ; Color AL
  rep stosb
  ret
person Sep Roland    schedule 04.05.2021
comment
тысмь за это! Но проблема в том, что две клавиши нельзя нажать одновременно, чтобы переместить обе доски одновременно, так что не знаю, исправит ли это ситуацию. Спасибо, я собираюсь использовать ваш код, чтобы мой проект стал в 20 раз лучше. Кстати, как изменить ширину доски? Также я действительно ничего не знаю об es или rep stosb, будет ли это проблемой? - person orraz1; 05.05.2021
comment
@orraz1 Если вы на самом деле ничего не знаете о es или rep stosb, то ознакомьтесь с полным набором инструкций 8086. Это не слишком сложно; он содержит около 50 инструкций, что ничто по сравнению с набором инструкций текущего x86. Я считаю, что мой второй ответ решит вашу проблему с одновременно нажатыми клавишами! - person Sep Roland; 07.05.2021