**
**	keys.a
**
**	The Key event handling and key mapping routines
**

	XREF	_DOSBase

	INCLUDE "intuition/intuition.i"
	INCLUDE "dos/dos.i"
	INCLUDE "dos/dos_lib.i"

	INCLUDE "envdata.i"     ;the environment-data structure definition

	INCLUDE "Z80_struct.i"


** -------------------------------------------------------------------------


** Key specification codes (byte):
**
**	Bit 7: Set to signal 'undefined'.
**	Bit 6: Set if code is extended.
**	Bits 5-3: Keyboard row number (0-7).
**	Bits 2-0: Data bit number. Only 0-4 are valid values.

SPKEY_UNDEF = $80
SPKEY_EXTEN = $40
SPKEY_ROW_0 = %000000
SPKEY_ROW_1 = %001000
SPKEY_ROW_2 = %010000
SPKEY_ROW_3 = %011000
SPKEY_ROW_4 = %100000
SPKEY_ROW_5 = %101000
SPKEY_ROW_6 = %110000
SPKEY_ROW_7 = %111000
SPKEY_BIT_0 = 0
SPKEY_BIT_1 = 1
SPKEY_BIT_2 = 2
SPKEY_BIT_3 = 3
SPKEY_BIT_4 = 4


	;A total of 40 keys on the Spectrum keyboard:

SPKEY_CAPS	= SPKEY_ROW_0|SPKEY_BIT_0
SPKEY_SYMBOL	= SPKEY_ROW_7|SPKEY_BIT_1
SPKEY_SPACE	= SPKEY_ROW_7|SPKEY_BIT_0
SPKEY_ENTER	= SPKEY_ROW_6|SPKEY_BIT_0
SPKEY_1 = SPKEY_ROW_3|SPKEY_BIT_0
SPKEY_2 = SPKEY_ROW_3|SPKEY_BIT_1
SPKEY_3 = SPKEY_ROW_3|SPKEY_BIT_2
SPKEY_4 = SPKEY_ROW_3|SPKEY_BIT_3
SPKEY_5 = SPKEY_ROW_3|SPKEY_BIT_4
SPKEY_6 = SPKEY_ROW_4|SPKEY_BIT_4
SPKEY_7 = SPKEY_ROW_4|SPKEY_BIT_3
SPKEY_8 = SPKEY_ROW_4|SPKEY_BIT_2
SPKEY_9 = SPKEY_ROW_4|SPKEY_BIT_1
SPKEY_0 = SPKEY_ROW_4|SPKEY_BIT_0
SPKEY_A = SPKEY_ROW_1|SPKEY_BIT_0
SPKEY_B = SPKEY_ROW_7|SPKEY_BIT_4
SPKEY_C = SPKEY_ROW_0|SPKEY_BIT_3
SPKEY_D = SPKEY_ROW_1|SPKEY_BIT_2
SPKEY_E = SPKEY_ROW_2|SPKEY_BIT_2
SPKEY_F = SPKEY_ROW_1|SPKEY_BIT_3
SPKEY_G = SPKEY_ROW_1|SPKEY_BIT_4
SPKEY_H = SPKEY_ROW_6|SPKEY_BIT_4
SPKEY_I = SPKEY_ROW_5|SPKEY_BIT_2
SPKEY_J = SPKEY_ROW_6|SPKEY_BIT_3
SPKEY_K = SPKEY_ROW_6|SPKEY_BIT_2
SPKEY_L = SPKEY_ROW_6|SPKEY_BIT_1
SPKEY_M = SPKEY_ROW_7|SPKEY_BIT_2
SPKEY_N = SPKEY_ROW_7|SPKEY_BIT_3
SPKEY_O = SPKEY_ROW_5|SPKEY_BIT_1
SPKEY_P = SPKEY_ROW_5|SPKEY_BIT_0
SPKEY_Q = SPKEY_ROW_2|SPKEY_BIT_0
SPKEY_R = SPKEY_ROW_2|SPKEY_BIT_3
SPKEY_S = SPKEY_ROW_1|SPKEY_BIT_1
SPKEY_T = SPKEY_ROW_2|SPKEY_BIT_4
SPKEY_U = SPKEY_ROW_5|SPKEY_BIT_3
SPKEY_V = SPKEY_ROW_0|SPKEY_BIT_4
SPKEY_W = SPKEY_ROW_2|SPKEY_BIT_1
SPKEY_X = SPKEY_ROW_0|SPKEY_BIT_2
SPKEY_Y = SPKEY_ROW_5|SPKEY_BIT_4
SPKEY_Z = SPKEY_ROW_0|SPKEY_BIT_1



** Mapping raw keycodes to Spectrum keys:
**
** A conversion table gives a key specification code, as defined above,
** for each raw keycode. If the SPKEY_UNDEF flag (bit 7) of the code is
** set, no action will be taken.
**
** If the SPKEY_EXTEN flag (bit 6) is set, the bits 5 to 0 index into a
** table with 32 entries of 2 pointers per entry. These are (unless they
** are zero) pointers to control sequences of byte codes. The first
** pointer is used if no Shift key is pressed (Caps Lock not included),
** and the second pointer otherwise.
**   The bytes in a sequence are interpreted one at a time, as follows:
** If bit 7 is set, the bits 5 to 0 specify a row/bit combination as above,
** and bit 6 is set if the key bit will be set (key Up) and reset if the
** key bit will be reset (key Down). Use the constants KSC_KEYUP and
** KSC_KEYDOWN bitwise OR:ed with the SPKEY_ constants to specify a
** keypress/release. Observe that these are absolute set/reset commands.
** They do not depend on combinations with other keys.
**   If bit 7 is reset, the bits 0 to 6 represent an action code:
**	0		End of sequence.
**	KSC_WAIT	Pause. (Waits about 1/16 second).
** The sequence end marker is thus always a zero byte. The alias KSC_END
** can be used, but will always remain equal to zero.
**   When a key with extended code is released, only the KSC_KEYDOWN
** operations are undone. (Key-Up never does any real damage). Also, *both*
** control sequences are undone each time, since we can't track what the
** shift status was when the key was pressed. When Caps Lock is released,
** the Caps Shift bit is always set (key Up), for much the same reason.
**
** If a key is not extended or undefined, it is looked up in the Capsable
** table. If its corresponding bit is reset, then the Caps Lock key
** status is ignored. Otherwise, if the key was pressed, then if Caps
** Lock is down and the Symbol Shift bit is set (not pressed), the Caps
** Shift key bit will be set (key Down). If the key was released, then if
** Caps Lock is down, the Caps Shift bit will be set (key Up).
**   After all this has been checked, the key itself is treated straight-
** forwardly using the translation table.


	;	Key Specification	Raw Code and Key
	;	Code			Symbol (American kbd.)
KeyTable
	dc.b	SPKEY_EXTEN|0		;00  ` ~  (left of 1)
	dc.b	SPKEY_1 		;01  1
	dc.b	SPKEY_2 		;02  2
	dc.b	SPKEY_3 		;03  3
	dc.b	SPKEY_4 		;04  4
	dc.b	SPKEY_5 		;05  5
	dc.b	SPKEY_6 		;06  6
	dc.b	SPKEY_7 		;07  7
	dc.b	SPKEY_8 		;08  8
	dc.b	SPKEY_9 		;09  9
	dc.b	SPKEY_0 		;0A  0
	dc.b	SPKEY_EXTEN|1		;0B  - _  (right of 0)
	dc.b	SPKEY_EXTEN|2		;0C  = +  (second right of 0)
	dc.b	SPKEY_EXTEN|3		;0D  \ |  (left of Back Space)
	dc.b	SPKEY_UNDEF		;0E  (undefined)
	dc.b	SPKEY_0 		;0F  NumPad 0
	dc.b	SPKEY_Q 		;10  Q
	dc.b	SPKEY_W 		;11  W
	dc.b	SPKEY_E 		;12  E
	dc.b	SPKEY_R 		;13  R
	dc.b	SPKEY_T 		;14  T
	dc.b	SPKEY_Y 		;15  Y
	dc.b	SPKEY_U 		;16  U
	dc.b	SPKEY_I 		;17  I
	dc.b	SPKEY_O 		;18  O
	dc.b	SPKEY_P 		;19  P
	dc.b	SPKEY_EXTEN|4		;1A  [ {  (right of P)
	dc.b	SPKEY_EXTEN|5		;1B  ] }  (second right of P)
	dc.b	SPKEY_UNDEF		;1C  (undefined)
	dc.b	SPKEY_1 		;1D  NumPad 1
	dc.b	SPKEY_2 		;1E  NumPad 2
	dc.b	SPKEY_3 		;1F  NumPad 3
	dc.b	SPKEY_A 		;20  A
	dc.b	SPKEY_S 		;21  S
	dc.b	SPKEY_D 		;22  D
	dc.b	SPKEY_F 		;23  F
	dc.b	SPKEY_G 		;24  G
	dc.b	SPKEY_H 		;25  H
	dc.b	SPKEY_J 		;26  J
	dc.b	SPKEY_K 		;27  K
	dc.b	SPKEY_L 		;28  L
	dc.b	SPKEY_EXTEN|6		;29  ; :  (right of L)
	dc.b	SPKEY_EXTEN|7		;2A  ' "  (second right of L)
	dc.b	SPKEY_EXTEN|8		;2B  ' *  (third right of L)     i)
	dc.b	SPKEY_UNDEF		;2C  (undefined)
	dc.b	SPKEY_4 		;2D  NumPad 4
	dc.b	SPKEY_5 		;2E  NumPad 5
	dc.b	SPKEY_6 		;2F  NumPad 6
	dc.b	SPKEY_EXTEN|9		;30  < >  (left of Z)		ii)
	dc.b	SPKEY_Z 		;31  Z
	dc.b	SPKEY_X 		;32  X
	dc.b	SPKEY_C 		;33  C
	dc.b	SPKEY_V 		;34  V
	dc.b	SPKEY_B 		;35  B
	dc.b	SPKEY_N 		;36  N
	dc.b	SPKEY_M 		;37  M
	dc.b	SPKEY_EXTEN|10		;38  , <  (right of M)
	dc.b	SPKEY_EXTEN|11		;39  . >  (second right of M)
	dc.b	SPKEY_EXTEN|12		;3A  / ?  (third right of M)
	dc.b	SPKEY_UNDEF		;3B  (undefined)
	dc.b	SPKEY_EXTEN|27		;3C  NumPad .
	dc.b	SPKEY_7 		;3D  NumPad 7
	dc.b	SPKEY_8 		;3E  NumPad 8
	dc.b	SPKEY_9 		;3F  NumPad 9
	dc.b	SPKEY_SPACE		;40  Space Bar
	dc.b	SPKEY_EXTEN|13		;41  Back Space
	dc.b	SPKEY_EXTEN|19		;42  Tab
	dc.b	SPKEY_ENTER		;43  NumPad Enter
	dc.b	SPKEY_ENTER		;44  Return
	dc.b	SPKEY_EXTEN|18		;45  Esc
	dc.b	SPKEY_EXTEN|20		;46  Del
	dc.b	SPKEY_UNDEF		;47  (undefined)
	dc.b	SPKEY_UNDEF		;48  (undefined)
	dc.b	SPKEY_UNDEF		;49  (undefined)
	dc.b	SPKEY_EXTEN|25		;4A  NumPad -
	dc.b	SPKEY_UNDEF		;4B  (undefined)
	dc.b	SPKEY_EXTEN|14		;4C  Up arrow
	dc.b	SPKEY_EXTEN|15		;4D  Down arrow
	dc.b	SPKEY_EXTEN|16		;4E  Right arrow
	dc.b	SPKEY_EXTEN|17		;4F  Left arrow
	dc.b	SPKEY_UNDEF		;50  F1
	dc.b	SPKEY_UNDEF		;51  F2
	dc.b	SPKEY_UNDEF		;52  F3
	dc.b	SPKEY_UNDEF		;53  F4
	dc.b	SPKEY_UNDEF		;54  F5
	dc.b	SPKEY_UNDEF		;55  F6
	dc.b	SPKEY_UNDEF		;56  F7
	dc.b	SPKEY_UNDEF		;57  F8
	dc.b	SPKEY_UNDEF		;58  F9
	dc.b	SPKEY_UNDEF		;59  F10
	dc.b	SPKEY_EXTEN|21		;5A  NumPad (  (above NumPad 7)
	dc.b	SPKEY_EXTEN|22		;5B  NumPad )  (above NumPad 8)
	dc.b	SPKEY_EXTEN|23		;5C  NumPad /
	dc.b	SPKEY_EXTEN|24		;5D  NumPad *
	dc.b	SPKEY_EXTEN|26		;5E  NumPad +
	dc.b	SPKEY_UNDEF		;5F  Help
	dc.b	SPKEY_CAPS		;60  Left Shift
	dc.b	SPKEY_CAPS		;61  Right Shift
	dc.b	SPKEY_UNDEF		;62  Caps Lock		       iii)
	dc.b	SPKEY_UNDEF		;63  Ctrl			iv)
	dc.b	SPKEY_SYMBOL		;64  Left Alt
	dc.b	SPKEY_SYMBOL		;65  Right Alt
	dc.b	SPKEY_UNDEF		;66  Left Amiga key		 v)
	dc.b	SPKEY_UNDEF		;67  Right Amiga key		 v)
SPKEY_LAST = $67

** Footnotes:
**
**   i) Key 2B does not exist on A1000 and most American keyboards.
**  ii) Key 30 does not exist on most American keyboards.
** iii) Caps Lock Up is caught before the table lookup. Down is undefined
**	since the qualifier field tells whether it is pressed anyhow.
**  iv) Ctrl- combinations are caught before the table lookup, and are
**	reserved for special emulator control keys.
**   v) There is no reason to react in any way on the Amiga keys. The
**	system handles that perfectly well.


	;Table of 8 bytes (0 to 7) corresponding to Spectrum
	;key rows 0 to 7. Bits 0 to 4 represent keys; bit 0 is
	;the outermost key in the row.
	;If the bit is set, the key is affected by Caps Shift.
Capsable
	dc.b	%11110	;V, C, X, Z, Caps Shift
	dc.b	%11111	;G, F, D, S, A
	dc.b	%11111	;T, R, E, W, Q
	dc.b	%00000	;5, 4, 3, 2, 1
	dc.b	%00000	;6, 7, 8, 9, 0
	dc.b	%11111	;Y, U, I, O, P
	dc.b	%11110	;H, J, K, L, Enter
	dc.b	%11100	;B, N, M, Symbol Shift, Space


	;Table of (at most) 32 * 2 pointers to control
	;sequences. Zero pointers are ignored.

	;	Unshifted, Shifted	Entry number	Mapped to key
Extensions
	dc.l	0,kseq_tilde		;0		` ~
	dc.l	kseq_minus,kseq_uscore	;1		- _
	dc.l	kseq_equal,kseq_plus	;2		= +
	dc.l	kseq_bslash,kseq_vbar	;3		\ |
	dc.l	kseq_lbrak,kseq_lbrace	;4		[ {
	dc.l	kseq_rbrak,kseq_rbrace	;5		] }
	dc.l	kseq_semic,kseq_colon	;6		; :
	dc.l	kseq_tick,kseq_dquote	;7		' "
	dc.l	kseq_tick,kseq_aster	;8		' *
	dc.l	kseq_lthan,kseq_gthan	;9		< >
	dc.l	kseq_comma,kseq_lthan	;10		, <
	dc.l	kseq_dot,kseq_gthan	;11		. >
	dc.l	kseq_slash,kseq_quest	;12		/ ?
	dc.l	kseq_bspace,0		;13		Back Space
	dc.l	kseq_up,0		;14		Up Arrow
	dc.l	kseq_down,0		;15		Down Arrow
	dc.l	kseq_right,0		;16		Right Arrow
	dc.l	kseq_left,0		;17		Left Arrow
	dc.l	kseq_e_mod,0		;18		Esc
	dc.l	kseq_tab,0		;19		Tab
	dc.l	kseq_bspace,0		;20		Del
	dc.l	kseq_lparen,0		;21		NumPad (
	dc.l	kseq_rparen,0		;22		NumPad )
	dc.l	kseq_slash,0		;23		NumPad /
	dc.l	kseq_aster,0		;24		NumPad *
	dc.l	kseq_minus,0		;25		NumPad -
	dc.l	kseq_plus,0		;26		NumPad +
	dc.l	kseq_dot,0		;27		NumPad .
	dc.l	0,0			;28		(unused)
	dc.l	0,0			;29		(unused)
	dc.l	0,0			;30		(unused)
	dc.l	0,0			;31		(unused)



** Extended keycode Control Sequences
**
** Do not make assumptions about what the status of the Caps Shift key
** is (that is, if they are mapped to a shifted combination or not).
** Always explicitly set Caps Shift to the necessary value. If this is
** done, remapping should be possible without changing the sequences
** themselves.
**   If a KSC_WAIT is done (e.g. wait for E-mode), another KSC_WAIT may
** have to be done before the sequence ends. Otherwise there might not
** be time enough for the Spectrum program to register the status of the
** last pressed key before it goes up again. (The key-up message could be
** sent even before the first KSC_WAIT has finished. It will then be
** processed almost immediately.)
**   The mappings correspond to a certain sequence of keypresses, rather
** than to a particular symbol. They do not attempt to exit from any
** current input mode first. For instance, if '\' is pressed when in
** E-mode, then Caps Shift and Symbol Shift will be pressed again (leaving
** E-mode this time), followed by a Caps Shift and 'D', producing 'STEP'.
** I do not consider this a misfeature. Pressing '\' in E-mode has no
** straightforward interpretation anyway.

** Key Sequence Codes
KSC_DOWN	= %10000000
KSC_UP		= %11000000
KSC_END 	= 0	;Always zero.
KSC_WAIT	= 1

kseq_tilde	dc.b KSC_DOWN|SPKEY_CAPS,KSC_DOWN|SPKEY_SYMBOL
		dc.b KSC_WAIT,KSC_UP|SPKEY_CAPS
		dc.b KSC_DOWN|SPKEY_A,KSC_WAIT,0

kseq_minus	dc.b KSC_UP|SPKEY_CAPS,KSC_DOWN|SPKEY_SYMBOL
		dc.b KSC_DOWN|SPKEY_J,0

kseq_uscore	dc.b KSC_UP|SPKEY_CAPS,KSC_DOWN|SPKEY_SYMBOL
		dc.b KSC_DOWN|SPKEY_0,0

kseq_equal	dc.b KSC_UP|SPKEY_CAPS,KSC_DOWN|SPKEY_SYMBOL
		dc.b KSC_DOWN|SPKEY_L,0

kseq_plus	dc.b KSC_UP|SPKEY_CAPS,KSC_DOWN|SPKEY_SYMBOL
		dc.b KSC_DOWN|SPKEY_K,0

kseq_bslash	dc.b KSC_DOWN|SPKEY_CAPS,KSC_DOWN|SPKEY_SYMBOL
		dc.b KSC_WAIT,KSC_UP|SPKEY_CAPS
		dc.b KSC_DOWN|SPKEY_D,KSC_WAIT,0

kseq_vbar	dc.b KSC_DOWN|SPKEY_CAPS,KSC_DOWN|SPKEY_SYMBOL
		dc.b KSC_WAIT,KSC_UP|SPKEY_CAPS
		dc.b KSC_DOWN|SPKEY_S,KSC_WAIT,0

kseq_lbrak	dc.b KSC_DOWN|SPKEY_CAPS,KSC_DOWN|SPKEY_SYMBOL
		dc.b KSC_WAIT,KSC_UP|SPKEY_CAPS
		dc.b KSC_DOWN|SPKEY_Y,KSC_WAIT,0

kseq_rbrak	dc.b KSC_DOWN|SPKEY_CAPS,KSC_DOWN|SPKEY_SYMBOL
		dc.b KSC_WAIT,KSC_UP|SPKEY_CAPS
		dc.b KSC_DOWN|SPKEY_U,KSC_WAIT,0

kseq_lbrace	dc.b KSC_DOWN|SPKEY_CAPS,KSC_DOWN|SPKEY_SYMBOL
		dc.b KSC_WAIT,KSC_UP|SPKEY_CAPS
		dc.b KSC_DOWN|SPKEY_F,KSC_WAIT,0

kseq_rbrace	dc.b KSC_DOWN|SPKEY_CAPS,KSC_DOWN|SPKEY_SYMBOL
		dc.b KSC_WAIT,KSC_UP|SPKEY_CAPS
		dc.b KSC_DOWN|SPKEY_G,KSC_WAIT,0

kseq_semic	dc.b KSC_UP|SPKEY_CAPS,KSC_DOWN|SPKEY_SYMBOL
		dc.b KSC_DOWN|SPKEY_O,0

kseq_colon	dc.b KSC_UP|SPKEY_CAPS,KSC_DOWN|SPKEY_SYMBOL
		dc.b KSC_DOWN|SPKEY_Z,0

kseq_tick	dc.b KSC_UP|SPKEY_CAPS,KSC_DOWN|SPKEY_SYMBOL
		dc.b KSC_DOWN|SPKEY_7,0

kseq_dquote	dc.b KSC_UP|SPKEY_CAPS,KSC_DOWN|SPKEY_SYMBOL
		dc.b KSC_DOWN|SPKEY_P,0

kseq_aster	dc.b KSC_UP|SPKEY_CAPS,KSC_DOWN|SPKEY_SYMBOL
		dc.b KSC_DOWN|SPKEY_B,0

kseq_lthan	dc.b KSC_UP|SPKEY_CAPS,KSC_DOWN|SPKEY_SYMBOL
		dc.b KSC_DOWN|SPKEY_R,0

kseq_gthan	dc.b KSC_UP|SPKEY_CAPS,KSC_DOWN|SPKEY_SYMBOL
		dc.b KSC_DOWN|SPKEY_T,0

kseq_comma	dc.b KSC_UP|SPKEY_CAPS,KSC_DOWN|SPKEY_SYMBOL
		dc.b KSC_DOWN|SPKEY_N,0

kseq_dot	dc.b KSC_UP|SPKEY_CAPS,KSC_DOWN|SPKEY_SYMBOL
		dc.b KSC_DOWN|SPKEY_M,0

kseq_slash	dc.b KSC_UP|SPKEY_CAPS,KSC_DOWN|SPKEY_SYMBOL
		dc.b KSC_DOWN|SPKEY_V,0

kseq_quest	dc.b KSC_UP|SPKEY_CAPS,KSC_DOWN|SPKEY_SYMBOL
		dc.b KSC_DOWN|SPKEY_C,0

kseq_bspace	dc.b KSC_UP|SPKEY_SYMBOL,KSC_DOWN|SPKEY_CAPS
		dc.b KSC_DOWN|SPKEY_0,0

kseq_up 	dc.b KSC_UP|SPKEY_SYMBOL,KSC_DOWN|SPKEY_CAPS
		dc.b KSC_DOWN|SPKEY_7,0

kseq_down	dc.b KSC_UP|SPKEY_SYMBOL,KSC_DOWN|SPKEY_CAPS
		dc.b KSC_DOWN|SPKEY_6,0

kseq_left	dc.b KSC_UP|SPKEY_SYMBOL,KSC_DOWN|SPKEY_CAPS
		dc.b KSC_DOWN|SPKEY_5,0

kseq_right	dc.b KSC_UP|SPKEY_SYMBOL,KSC_DOWN|SPKEY_CAPS
		dc.b KSC_DOWN|SPKEY_8,0

kseq_e_mod	dc.b KSC_DOWN|SPKEY_CAPS,KSC_DOWN|SPKEY_SYMBOL
		dc.b KSC_WAIT,KSC_UP|SPKEY_CAPS
		dc.b KSC_UP|SPKEY_SYMBOL,0

kseq_tab	dc.b KSC_DOWN|SPKEY_CAPS,KSC_DOWN|SPKEY_SYMBOL
		dc.b KSC_WAIT,KSC_UP|SPKEY_CAPS
		dc.b KSC_UP|SPKEY_SYMBOL,KSC_DOWN|SPKEY_P
		dc.b KSC_WAIT,0

kseq_lparen	dc.b KSC_UP|SPKEY_CAPS,KSC_DOWN|SPKEY_SYMBOL
		dc.b KSC_DOWN|SPKEY_8,0

kseq_rparen	dc.b KSC_UP|SPKEY_CAPS,KSC_DOWN|SPKEY_SYMBOL
		dc.b KSC_DOWN|SPKEY_9,0

		dc.b	KSC_END 	;a sentinel


** -------------------------------------------------------------------------


**
**	The keyboard routine itself, at last!
**

	EVEN


	;The exit code (I prefer to have it here, near the entry).

rawKey_exitEND	moveq	#1,d0	    ;signal "exit program, please"
		bra.s	rawKey_exit

rawKey_exitOK	moveq	#0,d0

	;D0 should contain a return flag.
rawKey_exit	movem.l (a7)+,d1/d2/d3/a2/a6
		rts



	;Here is the entry point

	XDEF rawKey

rawKey
	;a1 points to an IntuiMessage structure, otherwise scratch.
	;The value returned in d0 is nonzero if the program should exit.

		movem.l d1/d2/d3/a2/a6,-(a7)
		move.l	4.w,a6	;get ExecBase pointer


	;Copy data and reply

		move.w	im_Code(a1),d0
		move.w	im_Qualifier(a1),d1
		movem.w d0/d1,-(a7)
		jsr	_LVOReplyMsg(a6)	;message pointer in a1
		movem.w (a7)+,d0/d1


	;Ignore repeated messages

		move.w	d1,d2
		andi.w	#IEQUALIFIER_REPEAT,d2
		beq.s	.not_repeat
		bra	rawKey_exitOK


	;Handle any control key combinations

.not_repeat	move.w	d1,d2
		andi.w	#IEQUALIFIER_CONTROL,d2
		beq.s	.not_ctrl

		;FIXME: Ctrl- combinations are currently ignored.

		bra	rawKey_exitOK


	;Make a2 a base pointer to the Env_Kbd field
	;of the Z80_Envdata struct

.not_ctrl	move.l	_Zctrl,a2
		lea	Z80_Envdata+Envd_Kbd(a2),a2


	;Catch Caps Lock Up

		cmp.b	#$62+$80,d0	;Caps Lock up->Caps Shift up
		bne.s	.not_capsl_up
		moveq	#-1,d2		;key Up code
		move.b	#SPKEY_CAPS,d0	;key spec.
		bsr	rawKeySet
		bra rawKey_exitOK


	;Get on with the real decoding.
	;d0 is the keycode, d1 the qualifier

.not_capsl_up	bclr	#7,d0	;test and clear bit 7
		sne	d2	;d2 is FF if Key Up, otherwise 00
		ext.w	d0	;d0 word is 0000 to 007F

		cmp.w	#SPKEY_LAST,d0
		bge	rawKey_exitOK	;undefined if out of bounds


	;Table lookup

		lea	KeyTable(pc),a1
		move.b	(a1,d0.w),d0	;get key spec, d0 is 0000-00FF

		bmi	rawKey_exitOK	;undefined if bit 7 set

		bclr	#6,d0		;test and clear bit 6
		bne.s	rawKeyExt	;extended spec. if bit 6 set


	;We have a normal code, with bits 7 and 6 reset.

		;Calculate row and bit number
		move.w	d0,d3
		lsr.w	#3,d3		;row to d3
		and.w	#%111,d0	;bit number in d0

		;Test the Capsable table bit for the key
		lea	Capsable(pc),a1
		btst.b	d0,(a1,d3.w)
		beq.s	.norm_spec	;skip if not capsable

		;Different actions if key goes down or up
		tst.b	d2
		bne.s	.cbl_up


		;If capsable Down, examine Caps Lock
		andi.w	#IEQUALIFIER_CAPSLOCK,d1
		beq.s	.norm_spec	;skip if not Caps Lock down

		;Caps Lock was down. Ignore it if Symbol Shift is down
		btst.b	#1,7(a2)	;bit 1, row 7
		beq.s	.norm_spec	;skip if key Down (bit reset)

		;else key will be shifted - Caps Shift key Down
		bclr.b	#0,(a2) 	;bit 0, row 0 reset
		bra.s	.norm_spec	;handle the key itself


.cbl_up 	;If capsable Up, also examine Caps Lock
		andi.w	#IEQUALIFIER_CAPSLOCK,d1
		beq.s	.norm_spec	;skip if not Caps Lock down
		bset.b	#0,(a2) 	;else Caps Shift Up (set bit)


	;Now we handle the key specification on its own

.norm_spec	;We have d2 = FF if key Up, and 00 if key Down.
		;D0 is the bit number and d3 the row.

		tst.b	d2
		beq.s	.reset_bit

		bset.b	d0,(a2,d3.w)	;set key bit
		bra rawKey_exitOK	;return

.reset_bit	bclr.b	d0,(a2,d3.w)	;reset key bit
		bra rawKey_exitOK	;return


	;Handling of the Extended keycodes

rawKeyExt	;Offset table index (0-31) in d0 (bits 6 to 15 are reset)
		;d1 is the qualifier
		;d2 is FF if Key Up, otherwise 00

	;An entry consists of two pointers (8 bytes). The first one
	;(with the lower index) is used if no Shift key is pressed
	;(Caps Lock not counted), and the second otherwise.

		lsl.w	#3,d0	;8 bytes per entry
		lea	Extensions(pc),a0	;base of pointer table
		tst.b	d2
		beq.s	.ext_key_down


	;Key Up

		lea	(a0,d0.w),a0	;index into table
		move.l	(a0),d1 	;First pointer
		beq.s	.ext_ku_2nd	;(ignore zero ptr)
		bsr.s	.ext_ku_seq	;handle first sequence

.ext_ku_2nd	move.l	4(a0),d1	;Second pointer
		beq	rawKey_exitOK	;(ignore zero ptr)
		bsr.s	.ext_ku_seq	;handle 2nd sequence
		bra	rawKey_exitOK	;return

.ext_ku_seq	move.l	d1,a1	;subroutine. d1 points to sequence
		moveq	#-1,d2		;d2 signals "set bit" (key Up)
.ext_ku_loop	move.b	(a1)+,d0	;get code
		beq.s	.ext_ku_ret	;zero is end marker
		bpl.s	.ext_ku_loop	;ignore if bit 7 reset
		btst	#6,d0		;test bit 6
		bne.s	.ext_ku_loop	;ignore if key Up (set)
		bsr	rawKeySet	;otherwise invert (set bit)
		bra.s	.ext_ku_loop

.ext_ku_ret	rts


	;Key Down

.ext_key_down	;Key down. Check shift keys
		move.w	d1,d3
		and.w	#IEQUALIFIER_RSHIFT|IEQUALIFIER_LSHIFT,d3
		bne.s	.ext_shift

		;Not shifted - first pointer
		move.l	(a0,d0.w),d1
		bra.s	.ext_seq

.ext_shift	;Shifted - second pointer
		move.l	4(a0,d0.w),d1

.ext_seq	;d1 holds pointer, and flags are set
		beq	rawKey_exitOK	;ignore zero pointers
		movea.l d1,a1

.ext_kd_loop	move.b	(a1)+,d0	;get code
		beq	rawKey_exitOK	;zero is end marker
		bpl.s	.ext_opcode	;jump if bit 7 reset
		btst	#6,d0		;test bit 6
		sne	d2		;set d2 if bit 6 was set
		;spec. in d0, flag in d2
		bsr	rawKeySet	;set/reset key bit
		bra.s	.ext_kd_loop

.ext_opcode	;bit 7 is reset
		cmp.b	#KSC_WAIT,d0
		bne.s	.ext_kd_loop	;unknown op.


	;KSC_WAIT

		bsr	rawKeyDelay
		bra.s	.ext_kd_loop




	;Common subroutines:


rawKeySet	;Set/reset bit in Spectrum kbd table.
		;Row in bits 5-3 of d0, Key Bit number in bits 2-0.
		;  bits 7-6 are always reset for safety and ease.
		;d2 (byte) is 0 if the bit will be reset (Down),
		;  otherwise it will be set (Up).
		;Changes d0 and d3.

		andi.w	#$3F,d0 ;keep only bits 5-0
		move.w	d0,d3
		lsr.w	#3,d3		;row to d3
		and.w	#%111,d0	;bit number to d0
		tst.b	d2
		beq.s	.reset_bit
		bset.b	d0,(a2,d3.w)	;set key bit
		rts
.reset_bit	bclr.b	d0,(a2,d3.w)	;reset key bit
		rts



rawKeyDelay	;Pause so the Spectrum program gets a chance
		;to detect the key status. No registers are affected.

		movem.l d0-d1/a0-a1/a6,-(a7)
		move.l	_DOSBase,a6
		moveq	#(TICKS_PER_SECOND+8)>>4,d1	;1/16 second
		jsr	_LVODelay(a6)
		movem.l (a7)+,d0-d1/a0-a1/a6
		rts


** -------------------------------------------------------------------------

