CPCWiki forum

General Category => Programming => Topic started by: redbox on 11:52, 10 May 13

Title: Keyboard Buffer and Menus
Post by: redbox on 11:52, 10 May 13
I am writing a simple menu system with up/down/fire select controls.

It's running at 50hz so if I just scan the keyboard and change the menu selection it's far to quick and jumpy.

A simple solution appeared to be to insert a simple counter to only scan the keyboard/change selection every 2 or 3 frames.  But this is still a bit jumpy or laggy, no matter how much I try to refine it.

I thought about flushing the keyboard buffer, but if the user holds down the key it's still way to fast at 50hz... so, is there a better way of doing it?
Title: Re: Keyboard Buffer and Menus
Post by: arnoldemu on 12:44, 10 May 13
Quote from: redbox on 11:52, 10 May 13
I am writing a simple menu system with up/down/fire select controls.

It's running at 50hz so if I just scan the keyboard and change the menu selection it's far to quick and jumpy.

A simple solution appeared to be to insert a simple counter to only scan the keyboard/change selection every 2 or 3 frames.  But this is still a bit jumpy or laggy, no matter how much I try to refine it.

I thought about flushing the keyboard buffer, but if the user holds down the key it's still way to fast at 50hz... so, is there a better way of doing it?
keep track of current and previous state of keyboard.
20 bytes total.

Then trigger movement/select on either key up (was pressed before, but not now), or key down (not pressed before, but pressed now).

That works great for any frame rate but there is no repeat.

So for repeat, check for key down. First time trigger a movement, then reset timer. when timer has done, do another movement, and reset timer.

repeat until key is up.
Title: Re: Keyboard Buffer and Menus
Post by: redbox on 14:00, 10 May 13
Thanks for the algorithms, will give them a try!
Title: Re: Keyboard Buffer and Menus
Post by: redbox on 11:38, 14 May 13
Quote from: arnoldemu on 12:44, 10 May 13
keep track of current and previous state of keyboard.
Then trigger movement/select on either key up (was pressed before, but not now), or key down (not pressed before, but pressed now).

I implemented this so thought I'd share the code.  I am using a mini-buffer of 1 byte as I only want to use 3 keys (Cursor Up/Down and Space) for the menu system.

Buffer byte is arranged as %01010100. Bits 7/5/3 are previous state of Cursor Up/Down/Space.  Bits 6/4/2 are current state of Cursor Up/Down/Space.

So basically I am looking for a %01 on 7/6, 5/4 or 3/2 to trigger an action.  If you hold down the key, the buffer will show %11 (so no action), and eventually for one frame %10 and again, no action is taken here.

However, from what you said above, I should also do the action on %10 ("key up - was pressed before, but not now") - but wouldn't this result in a double action...?

Anyway, the following code appears to be working for me:


menu_check_keys:    ld    a,(menu_keyb_buffer)        ; get state of keyboard buffer
            rla                    ; rotate left so current state becomes previous
            ld    (menu_keyb_buffer),a        ; store it back

            ld     a,&40                ; bit line to scan on keyboard
            call     scan_keyb            ; scan keyboard
            bit    0,a
            jp    z,menu_check_keys_up        ; up pressed, check buffer
            bit    2,a   
            jp    z,menu_check_keys_down        ; etc
            ld    a,&45
            call    scan_keyb
            bit    7,a
            jp    z,menu_check_keys_space

            ld    a,(menu_keyb_buffer)        ; nothing pressed, so set state in buffer
            res    6,a                ; reset bit 6 (cursor up current)   
            res    4,a                ; reset bit 4 (cursor down current)
            res    2,a                ; reset bit 2 (space current)
            ld    (menu_keyb_buffer),a        ; and store it back

            ret

menu_check_keys_up:    ld    a,(menu_keyb_buffer)       
            set    6,a                ; set bit 6 (cursor up current)
            ld    (menu_keyb_buffer),a
            cp    %01000000            ; is this first time it's been pressed?
            jp    z,menu_cursor_up        ; yes, then do action
            ret                    ; else RETurn and do nothing

menu_check_keys_down:    ld    a,(menu_keyb_buffer)
            set    4,a                ; set bit 4 (cursor down current)
            ld    (menu_keyb_buffer),a
            cp    %00010000
            jp    z,menu_cursor_down
            ret

menu_check_keys_space:    ld    a,(menu_keyb_buffer)
            set    2,a                ; set bit 2 (space current)
            ld    (menu_keyb_buffer),a
            cp    %00000100
            jp    z,menu_space
            ret

Title: Re: Keyboard Buffer and Menus
Post by: arnoldemu on 13:01, 14 May 13
Quote from: redbox on 11:38, 14 May 13
However, from what you said above, I should also do the action on %10 ("key up - was pressed before, but not now") - but wouldn't this result in a double action...?
Do the action on one or the other. I normally do it on key up.

I normally do something like this this:


ld hl,current_buffer
ld de,old_buffer
ld bc,10
ldir
;; update current
call scan_keyboard

ld e,1 ;; key mask
ld ix,old_buffer
ld a,(ix+0)  ;; new state (key line 0)
ld c,a
xor (ix+10) ;; xor with old state of key line 0 -> 1 here means key changed
and e ;; mask to isolate 1 key
;; 0 = no change, not zero = change
ret z
;; get new state
ld a,c
and e ;; the state, 0 means released, 1 means pressed.
ret




current_buffer:
defs 10
old_buffer:
defs 10
Title: Re: Keyboard Buffer and Menus
Post by: redbox on 14:44, 14 May 13
Ah I see, one OR the other - I misread what you said.

Thanks for the example code, we're doing the same thing but the other way round ;)
Title: Re: Keyboard Buffer and Menus
Post by: db6128 on 12:01, 20 May 13
Philosophical or, not logical OR. For that, you would need XOR. ;)
Title: Re: Keyboard Buffer and Menus
Post by: redbox on 12:08, 20 May 13
Quote from: db6128 on 12:01, 20 May 13
Philosophical or, not logical OR. For that, you would need XOR. ;)

Usually in my case it's XOR A.

Set Z flag and try again.   ;)
Title: Re: Keyboard Buffer and Menus
Post by: db6128 on 15:47, 20 May 13
Actually, I was thinking of XORing pastState against presentState and looking for a 1, but that would induce the response on both key-down and key-up . . . probably not what you want here, although maybe a good idea to hack into games like Supertest. :D

But yeah, good old XOR A! I usually use that for clearing A and/or setting Z, OR A for checking whether A is 0, and AND A for clearing C. Some of those side-effects overlap, but using certain instructions for certain effects helps me to remember what I was intending at the time of writing. I like the ability of XOR A to conjure up a zero quickly. I remember using something like XOR A:LD H,A:LD L,A:LD (HL),A:LD E,A:LDIR somewhere!

I was reading a manual for a different CPU the other day, and it had built-in commands for setting each of its registers to 0 instantly. Posers! ;)
Title: Re: Keyboard Buffer and Menus
Post by: ralferoo on 17:03, 20 May 13
My favourite instruction is "SBC A" which replicates the carry bit to all bits of A, this can be used for all sorts of nice tricks, e.g. "SBC A:AND option2-option1:ADD option1" to set A to option1 for no carry, and option2 for carry.
Title: Re: Keyboard Buffer and Menus
Post by: redbox on 17:12, 20 May 13
@db6128 - yes but always remember OR A destroys A (that's caught me out more than once!)

@ralferoo - that's really cool, will use that as my screen address routine sets carry if a pre shift is required and my routine is less than eloquent at present...!!!
Title: Re: Keyboard Buffer and Menus
Post by: ralferoo on 17:31, 20 May 13
Quote from: redbox on 17:12, 20 May 13
@db6128 - yes but always remember OR A destroys A (that's caught me out more than once!)
No it doesn't! XOR A sets A to 0. OR A / AND A both preserve A. Both instructions are effectively equivalent and only useful for modifying flags (and mostly only Z or S are useful, although clearing C is a nice side effect).
Title: Re: Keyboard Buffer and Menus
Post by: redbox on 18:06, 20 May 13
Hmmm you're right.

I remember fixing something with CP 0 instead, must have been a flag issue.

I'm sure seeing one optimisation that does destroy A though, but that obviously wasn't it. Z80 is starting to fry my brain ;)
Title: Re: Keyboard Buffer and Menus
Post by: db6128 on 11:12, 21 May 13
Quote from: ralferoo on 17:03, 20 May 13My favourite instruction is "SBC A" which replicates the carry bit to all bits of A, this can be used for all sorts of nice tricks, e.g. "SBC A:AND option2-option1:ADD option1" to set A to option1 for no carry, and option2 for carry.
Now this looks very clever! :D Thanks for the tip! How did you work that out?

It reminds me of some other, albeit less impressive, snippets that I picked up in the past, although I think they was based upon A interacting with other registers rather than itself. The latter makes for much more interesting tricks! On first glance, the self-referential operations can seem pointless, so it is always great to learn of uses for them. Especially if you love every chance for optimisation like I do!

Quote from: redbox on 17:12, 20 May 13@db6128 - yes but always remember OR A destroys A (that's caught me out more than once!)
I was going to respond to this but lost my connection and gave up, so ralferoo beat me to it. ;)

Quote from: ralferoo on 17:31, 20 May 13No it doesn't! XOR A sets A to 0. OR A / AND A both preserve A. Both instructions are effectively equivalent and only useful for modifying flags (and mostly only Z or S are useful, although clearing C is a nice side effect).
Yes, especially since we got a command to set C but not clear it. . .with the misleadingly named CCF actually meaning complement carry flag. :|

Anyway, they are both identical as you said, and I tend to use this otherwise useless property so that it is easier to remember what I was doing in ASM. . .although I confess that I forget which I used in which circumstances and might not always have been consistent!

Anyway, I always loved it when I got to a stretch of repeated code where I could only save 4 NOPs if C was already clear, and lo and behold, there was coincidentally an OR or an AND just a few bytes earlier.  :laugh:
Title: Re: Keyboard Buffer and Menus
Post by: ralferoo on 13:52, 21 May 13
Quote from: db6128 on 11:12, 21 May 13
Now this looks very clever! :D Thanks for the tip! How did you work that out?
The "SBC A: AND x: OR/ADD/XOR y" trick I've started doing relatively recently, but it was a fairly obvious extension once you know about "SBC A".

I can't remember when I first started using "SBC A" on Z80. I might have known about it when I was younger, but I suspect it's something I picked up when programming for the PS3 - there, every instruction is 1 cycle long but a branch will cause a 12 cycle penalty. So, you have instructions like "set-if-condition" that sets the register to 0 or all 1s depending if the condition is met and a "select" instruction where "selb rt,ra,rb,rm" is "rt=(ra and rm) or (rb and not rm)". So, you calculate both branches and just merge the results at the end using "Scc:SELB".

On the Z80 where there's no instruction pipline, the branches make a lot less of an impact, so it's usually better to branch except if you're doing exactly cycle counted code when it's more useful to have consistency.

The 68000 has a similar set-if-condition instruction, but less opportunities to use it, if I recall. I definitely remember doing things like explicit SEQ:AND:NOT:AND:OR on 68000, although I don't ever recall optimising this to SEQ:AND:XOR.

It's interesting that the same trick can be done on the 8086 too: "SBB AL,AL" is the equivalent, but there's also an "undocumented" "SALC" instruction that does the same but without affecting the flags, which suggests that the subtract-with-carry trick wasn't widely known at Intel when the 8086 was designed.
Title: Re: Keyboard Buffer and Menus
Post by: MaV on 16:20, 21 May 13
ralferoo = Programming God :D

Anyone who programs the Z80, 68000 and the Cell chip and know a heavy dose of x86 is one in my book!

Need I mention ralf does FPGAs as well?
Title: Re: Keyboard Buffer and Menus
Post by: redbox on 08:30, 02 June 13
Quote from: ralferoo on 17:31, 20 May 13
No it doesn't! XOR A sets A to 0. OR A / AND A both preserve A. Both instructions are effectively equivalent and only useful for modifying flags (and mostly only Z or S are useful, although clearing C is a nice side effect).

Remembered the one I was thinking of - instead of CP 1 use DEC A, but this does modify A.

:)
Title: Re: Keyboard Buffer and Menus
Post by: Gryzor on 20:53, 09 June 13
Quote from: MaV
Need I mention ralf does FPGAs as well?

Maybe we should collectively ask him to do a CPC for the MiST board? :D
Powered by SMFPacks Menu Editor Mod