-
Notifications
You must be signed in to change notification settings - Fork 4
/
main.S.pl
executable file
·580 lines (472 loc) · 21.2 KB
/
main.S.pl
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
#!/usr/bin/perl
use strict;
BEGIN {
do "AVR.pm";
die $@ if ($@);
}
BEGIN {
emit ".section .bss\n";
#make the button event queue be 256 byte aligned, so we can easily use mod 256 arithmetic
#we should already be aligned since this should be the beginning of the .bss section, so
#this is mostly informational
emit ".align 8\n";
#a queue to hold the button press and release events generated by logic associated with timer3
#The lower 6 bits of each item hold the button index, while the MSB indicates if it was a
#press (1) or release (0) event
#We shouldn't near this much space, but it makes the math much cheaper
#since we get mod 256 arithmetic "for free"
memory_variable "button_event_queue", 0x100;
#The head and tail of the button queue
memory_variable "button_event_head", 1;
memory_variable "button_event_tail", 1;
#done in begin section, so that declared constants can be accessed further down
memory_variable "current_configuration";
#the current protocol (0=boot, 1=report)
memory_variable "current_protocol";
#the current idle period, in ms
memory_variable "hid_idle_period", 2;
#the number of ms until the hid idle period expires and we have to re-send a report
memory_variable "hid_idle_ms_remaining", 2;
#contains the button states for each selector value
#the button states are stored in the low nibble of each byte.
#The high nibbles are not used
memory_variable "button_states", 13;
#contains the current state of the hid report
memory_variable "current_report", 22;
#the length of the key array in the report
#This is needed to be able to switch between the boot and report protocols
memory_variable "key_array_length", 1;
#An array with an entry for each modifier key, which contains a count of the number
#of keys currently pressed that "virtually" press that modifier key (like the # key,
#which is actually the 3 key with a virtual shift). If both the # and $ keys were
#pressed the count for the lshift modifier would be 2
memory_variable "modifier_virtual_count", 8;
#A bitmask that specifies which modifier keys are currently being physically pressed
#This does not take into account any modifier keys that are only being "virtually"
#pressed (see comments for modifier_virtual_count)
memory_variable "modifier_physical_status", 1;
#The address of the press table for the current keyboard mode
memory_variable "current_press_table", 2;
#The address of the press table for the "persistent" mode - that is, the mode that we go back
#to after a temporary mode switch (i.e. the nas button)
memory_variable "persistent_mode_press_table", 2;
#The LED state associated with the persistent mode
memory_variable "persistent_mode_leds", 1;
#This contains a 2-byte entry for each button, which is the address of a routine to
#execute when the button is released. The entry for a button is updated when the button
#is pressed, to reflect the correct routine to use when it is released
#In this way, we can correctly handle button releases when the mode changes while a
#button is pressed
memory_variable "release_table", 104;
emit "end_bss:\n";
# put boot log outside of bss, so that it doesn't get cleared on reset
memory_variable "boot_log_position", 2;
memory_variable "boot_log", 4*1024;
emit ".text\n";
}
use constant BUTTON_RELEASE => 0;
use constant BUTTON_PRESS => 1;
use constant LCTRL_OFFSET => 0;
use constant LSHIFT_OFFSET => 1;
use constant LALT_OFFSET => 2;
use constant LGUI_OFFSET => 3;
use constant RCTRL_OFFSET => 4;
use constant RSHIFT_OFFSET => 5;
use constant RALT_OFFSET => 6;
use constant RGUI_OFFSET => 7;
use constant RH_LED_MASK => 0b00001111;
use constant LH_LED_MASK => 0b11110000;
use constant HOST_LED_MASK => 0b11010000;
use constant LED_NAS => 0;
use constant LED_NORMAL => 1;
use constant LED_FUNC => 2;
use constant LED_10K => 3;
use constant LED_CAPS_LOCK => 4;
use constant LED_MOUSE => 5;
use constant LED_NUM_LOCK => 6;
use constant LED_SCROLL_LOCK => 7;
use constant BOOT_LOG_ENABLED => 0;
use constant KEY_ARRAY_OFFSET => 2;
do "descriptors.pm";
die $@ if ($@);
do "usb.pm";
die $@ if ($@);
do "timer.pm";
die $@ if ($@);
do "actions.pm";
die $@ if ($@);
sub dequeue_input_event;
sub process_input_event;
sub process_hid_idle;
emit_global_sub "main", sub {
#initialize the stack pointer
_ldi r16, 0xFF;
_sts SPL, r16;
_ldi r16, 0x20;
_sts SPH, r16;
#set clock to max speed (16mhz)
SET_CLOCK_SPEED r16, CLOCK_DIV_1;
CONFIGURE_GPIO(port=>GPIO_PORT_A, pin=>PIN_0, dir=>GPIO_DIR_IN, pullup=>GPIO_PULLUP_ENABLED);
CONFIGURE_GPIO(port=>GPIO_PORT_A, pin=>PIN_1, dir=>GPIO_DIR_IN, pullup=>GPIO_PULLUP_ENABLED);
CONFIGURE_GPIO(port=>GPIO_PORT_A, pin=>PIN_2, dir=>GPIO_DIR_IN, pullup=>GPIO_PULLUP_ENABLED);
CONFIGURE_GPIO(port=>GPIO_PORT_A, pin=>PIN_3, dir=>GPIO_DIR_IN, pullup=>GPIO_PULLUP_ENABLED);
CONFIGURE_GPIO(port=>GPIO_PORT_A, pin=>PIN_4, dir=>GPIO_DIR_IN, pullup=>GPIO_PULLUP_ENABLED);
CONFIGURE_GPIO(port=>GPIO_PORT_A, pin=>PIN_5, dir=>GPIO_DIR_IN, pullup=>GPIO_PULLUP_ENABLED);
CONFIGURE_GPIO(port=>GPIO_PORT_A, pin=>PIN_6, dir=>GPIO_DIR_IN, pullup=>GPIO_PULLUP_ENABLED);
CONFIGURE_GPIO(port=>GPIO_PORT_A, pin=>PIN_7, dir=>GPIO_DIR_IN, pullup=>GPIO_PULLUP_ENABLED);
CONFIGURE_GPIO(port=>GPIO_PORT_B, pin=>PIN_0, dir=>GPIO_DIR_IN, pullup=>GPIO_PULLUP_ENABLED);
CONFIGURE_GPIO(port=>GPIO_PORT_B, pin=>PIN_1, dir=>GPIO_DIR_IN, pullup=>GPIO_PULLUP_ENABLED);
CONFIGURE_GPIO(port=>GPIO_PORT_B, pin=>PIN_2, dir=>GPIO_DIR_IN, pullup=>GPIO_PULLUP_ENABLED);
CONFIGURE_GPIO(port=>GPIO_PORT_B, pin=>PIN_3, dir=>GPIO_DIR_IN, pullup=>GPIO_PULLUP_ENABLED);
CONFIGURE_GPIO(port=>GPIO_PORT_B, pin=>PIN_4, dir=>GPIO_DIR_IN, pullup=>GPIO_PULLUP_ENABLED);
CONFIGURE_GPIO(port=>GPIO_PORT_B, pin=>PIN_5, dir=>GPIO_DIR_IN, pullup=>GPIO_PULLUP_ENABLED);
CONFIGURE_GPIO(port=>GPIO_PORT_B, pin=>PIN_6, dir=>GPIO_DIR_IN, pullup=>GPIO_PULLUP_ENABLED);
CONFIGURE_GPIO(port=>GPIO_PORT_B, pin=>PIN_7, dir=>GPIO_DIR_IN, pullup=>GPIO_PULLUP_ENABLED);
CONFIGURE_GPIO(port=>GPIO_PORT_C, pin=>PIN_0, dir=>GPIO_DIR_OUT);
CONFIGURE_GPIO(port=>GPIO_PORT_C, pin=>PIN_1, dir=>GPIO_DIR_OUT);
CONFIGURE_GPIO(port=>GPIO_PORT_C, pin=>PIN_2, dir=>GPIO_DIR_OUT);
CONFIGURE_GPIO(port=>GPIO_PORT_C, pin=>PIN_3, dir=>GPIO_DIR_OUT);
CONFIGURE_GPIO(port=>GPIO_PORT_C, pin=>PIN_4, dir=>GPIO_DIR_OUT);
CONFIGURE_GPIO(port=>GPIO_PORT_C, pin=>PIN_5, dir=>GPIO_DIR_OUT);
CONFIGURE_GPIO(port=>GPIO_PORT_C, pin=>PIN_6, dir=>GPIO_DIR_OUT);
CONFIGURE_GPIO(port=>GPIO_PORT_C, pin=>PIN_7, dir=>GPIO_DIR_OUT);
CONFIGURE_GPIO(port=>GPIO_PORT_D, pin=>PIN_0, dir=>GPIO_DIR_OUT);
CONFIGURE_GPIO(port=>GPIO_PORT_D, pin=>PIN_1, dir=>GPIO_DIR_OUT);
CONFIGURE_GPIO(port=>GPIO_PORT_D, pin=>PIN_2, dir=>GPIO_DIR_OUT);
CONFIGURE_GPIO(port=>GPIO_PORT_D, pin=>PIN_3, dir=>GPIO_DIR_OUT);
CONFIGURE_GPIO(port=>GPIO_PORT_D, pin=>PIN_4, dir=>GPIO_DIR_IN, pullup=>GPIO_PULLUP_ENABLED);
CONFIGURE_GPIO(port=>GPIO_PORT_D, pin=>PIN_5, dir=>GPIO_DIR_IN, pullup=>GPIO_PULLUP_ENABLED);
CONFIGURE_GPIO(port=>GPIO_PORT_D, pin=>PIN_6, dir=>GPIO_DIR_IN, pullup=>GPIO_PULLUP_ENABLED);
CONFIGURE_GPIO(port=>GPIO_PORT_D, pin=>PIN_7, dir=>GPIO_DIR_IN, pullup=>GPIO_PULLUP_ENABLED);
CONFIGURE_GPIO(port=>GPIO_PORT_E, pin=>PIN_0, dir=>GPIO_DIR_IN, pullup=>GPIO_PULLUP_ENABLED);
CONFIGURE_GPIO(port=>GPIO_PORT_E, pin=>PIN_1, dir=>GPIO_DIR_IN, pullup=>GPIO_PULLUP_ENABLED);
CONFIGURE_GPIO(port=>GPIO_PORT_E, pin=>PIN_2, dir=>GPIO_DIR_IN, pullup=>GPIO_PULLUP_ENABLED);
CONFIGURE_GPIO(port=>GPIO_PORT_E, pin=>PIN_3, dir=>GPIO_DIR_IN, pullup=>GPIO_PULLUP_ENABLED);
CONFIGURE_GPIO(port=>GPIO_PORT_E, pin=>PIN_4, dir=>GPIO_DIR_IN, pullup=>GPIO_PULLUP_ENABLED);
CONFIGURE_GPIO(port=>GPIO_PORT_E, pin=>PIN_5, dir=>GPIO_DIR_IN, pullup=>GPIO_PULLUP_ENABLED);
CONFIGURE_GPIO(port=>GPIO_PORT_E, pin=>PIN_6, dir=>GPIO_DIR_IN, pullup=>GPIO_PULLUP_ENABLED);
CONFIGURE_GPIO(port=>GPIO_PORT_E, pin=>PIN_7, dir=>GPIO_DIR_IN, pullup=>GPIO_PULLUP_ENABLED);
CONFIGURE_GPIO(port=>GPIO_PORT_F, pin=>PIN_0, dir=>GPIO_DIR_IN, pullup=>GPIO_PULLUP_ENABLED);
CONFIGURE_GPIO(port=>GPIO_PORT_F, pin=>PIN_1, dir=>GPIO_DIR_IN, pullup=>GPIO_PULLUP_ENABLED);
CONFIGURE_GPIO(port=>GPIO_PORT_F, pin=>PIN_2, dir=>GPIO_DIR_IN, pullup=>GPIO_PULLUP_ENABLED);
CONFIGURE_GPIO(port=>GPIO_PORT_F, pin=>PIN_3, dir=>GPIO_DIR_IN, pullup=>GPIO_PULLUP_ENABLED);
CONFIGURE_GPIO(port=>GPIO_PORT_F, pin=>PIN_4, dir=>GPIO_DIR_IN, pullup=>GPIO_PULLUP_ENABLED);
CONFIGURE_GPIO(port=>GPIO_PORT_F, pin=>PIN_5, dir=>GPIO_DIR_IN, pullup=>GPIO_PULLUP_ENABLED);
CONFIGURE_GPIO(port=>GPIO_PORT_F, pin=>PIN_6, dir=>GPIO_DIR_IN, pullup=>GPIO_PULLUP_ENABLED);
CONFIGURE_GPIO(port=>GPIO_PORT_F, pin=>PIN_7, dir=>GPIO_DIR_IN, pullup=>GPIO_PULLUP_ENABLED);
if (BOOT_LOG_ENABLED) {
#initialize the boot log
_ldi r16, lo8("boot_log");
_sts "boot_log_position", r16;
_ldi r16, hi8("boot_log");
_sts "boot_log_position + 1", r16;
}
#initialize register with commonly used "zero" value
_clr r15_zero;
_call "reset";
usb_init();
#timer1_init();
timer3_init();
enable_timer3(r16);
#enable interrupts
_sei;
emit_sub "main_loop", sub {
block {
_lds r16, "current_configuration";
_cpi r16, 0;
_breq block_begin;
#wait for an input event and dequeue it
dequeue_input_event;
block {
_brtc block_end;
#process the dequeued event
process_input_event;
};
process_hid_idle;
#and do it all over again
_rjmp block_begin;
};
};
};
emit_sub "reset", sub {
if (BOOT_LOG_ENABLED) {
_ldi r24, 0xff;
_call "write_boot_log";
_call "write_boot_log";
}
#clear all LEDs
_ldi r16, 0xFF;
_out IO(PORTC), r16;
_sts "persistent_mode_leds", r16;
#reset all allocated memory to 0
_ldi zl, 0x00;
_ldi zh, 0x01;
block {
_st "z+", r15_zero;
_cpi zl, lo8("end_bss");
_brne block_begin;
_cpi zh, hi8("end_bss");
_brne block_begin;
};
#initialize the protocol
_ldi r16, 0x01;
_sts "current_protocol", r16;
_ldi r16, 0x14;
_sts "key_array_length", r16;
#initialize the idle rate to 500ms
_ldi r16, 0xF4;
_sts "hid_idle_period", r16;
_sts "hid_idle_ms_remaining", r16;
_ldi r16, 0x01;
_sts "hid_idle_period + 1", r16;
_sts "hid_idle_ms_remaining + 1", r16;
#initialize the press tables
_ldi r16, lo8(press_table_label("normal"));
_sts "current_press_table", r16;
_sts "persistent_mode_press_table", r16;
_ldi r16, hi8(press_table_label("normal"));
_sts "current_press_table + 1", r16;
_sts "persistent_mode_press_table + 1", r16;
_ret;
};
#Checks for an input event, and dequeues it into r16 if available
sub dequeue_input_event {
block {
_cli;
_ldi zh, hi8("button_event_queue");
_lds zl, "button_event_head";
_lds r16, "button_event_tail";
block {
_cp zl, r16;
_breq block_end;
_ld r16, "z+";
_sts "button_event_head", zl;
#set T flag to indicate that we have an event to process
_set;
_sei;
_rjmp block_end parent;
};
#clear the T flag to indicate that there is no event to process
_clt;
_sei;
_rjmp block_begin;
};
}
sub process_input_event {
block {
#we've got the input event in r16
#extract the button index and store it in r17
_mov r17, r16;
_cbr r17, 0x80;
#we really only need index*2 for address offsets/lookups (which are 2 bytes each)
_lsl r17;
block {
block {
#is it a press or release?
_sbrc r16, 7;
_rjmp block_end;
#it's a release event. Load the handler address from the release table
_ldi zl, lo8("release_table");
_ldi zh, hi8("release_table");
_add zl, r17;
_adc zh, r15_zero;
_ld r18, "z+";
_ld r19, "z";
_movw zl, r18;
_rjmp block_end parent;
};
#it's a press event. Load the address for the current press table
_lds zl, "current_press_table";
_lds zh, "current_press_table+1";
#calculate and store the location in the release table, based on button index
_ldi yl, lo8("release_table");
_ldi yh, hi8("release_table");
_add yl, r17;
_adc yh, r15_zero;
#lookup the handler address from the table
_add zl, r17;
_adc zh, r15_zero;
_lpm r16, "z+";
_lpm r17, "z";
_movw zl, r16;
};
_icall;
};
}
sub process_hid_idle {
block {
#first, check if the idle period is enabled
_lds r16, "hid_idle_period";
_lds r17, "hid_idle_period + 1";
_cp r16, r15_zero;
_cpc r17, r15_zero;
_breq block_end;
#next, check if the current idle period has elapsed
_lds r16, "hid_idle_ms_remaining";
_lds r17, "hid_idle_ms_remaining + 1";
_cp r16, r15_zero;
_cpc r17, r15_zero;
_breq block_end;
_call "send_hid_report";
};
}
#key map for normal mode
my(%normal_key_map) = (
# d n e s w
r1 => finger_map("h", "g", "'", "m", "d"),
r2 => finger_map("t", "w", "`", "c", "f"),
r3 => finger_map("n", "v", undef, "r", "b"),
r4 => finger_map("s", "z", "\\", "l", ")"),
# d dd u in lo uo
rt => thumb_map("nas", "naslock", "func", "sp", "lalt", "bksp"),
# d n e s w
l1 => finger_map("u", "q", "i", "p", "\""),
l2 => finger_map("e", ".", "y", "j", "`"),
l3 => finger_map("o", ",", "x", "k", "esc"),
l4 => finger_map("a", "/", "(", ";", "del"),
# d dd u in lo uo
lt => thumb_map("lshift", "capslock", "norm", "ret", "lctrl", "tab")
);
#key map for when the normal mode key is held down
#It's similar to normal mode, except that we add some shortcuts for
#ctrl+c, ctrl+v, ctrl+x, etc.
my(%normal_hold_key_map) = (
# d n e s w
r1 => finger_map("h", "g", "'", "m", "d"),
r2 => finger_map("t", "w", "`", "c", "f"),
r3 => finger_map("n", "v", undef, "r", "b"),
r4 => finger_map("s", "z", "\\", "l", ")"),
# d dd u in lo uo
rt => thumb_map("nas", "naslock", "func", "sp", "lalt", "bksp"),
# d n e s w
l1 => finger_map("u", "q", "i", "ctrlv", "\""),
l2 => finger_map("e", ".", "y", "ctrlc", "`"),
l3 => finger_map("o", ",", "x", "ctrlx", "esc"),
l4 => finger_map("norm", "/", "(", "game", "del"),
# d dd u in lo uo
lt => thumb_map("lshift", "capslock", "norm", "ret", "lctrl", "tab")
);
my(%nas_key_map) = (
# d n e s w
r1 => finger_map("7", "&", undef, "+", "6"),
r2 => finger_map("8", "*", undef, undef, "^"),
r3 => finger_map("9", "[", "menu", undef, undef),
r4 => finger_map("0", "]", undef, undef, "}"),
# d dd u in lo uo
rt => thumb_map("nas", "naslock", "func", "sp", "lalt", "bksp"),
# d n e s w
l1 => finger_map("4", "\$", "5", "-", undef),
l2 => finger_map("3", "#", undef, "%", undef),
l3 => finger_map("2", "@", undef, undef, "esc"),
l4 => finger_map("1", "!", "{", "=", "del"),
# d dd u in lo uo
lt => thumb_map("lshift", "capslock", "norm", "ret", "lctrl", "tab")
);
my(%func_key_map) = (
# d n e s w
r1 => finger_map("home", "up", "right", "down", "left"),
r2 => finger_map(undef, "f8", undef, "f7", "end"),
r3 => finger_map("printscreen", "f10", "lgui", "f9", "ins"),
r4 => finger_map("pause", "pgup", "f12", "pgdn", "f11"),
# d dd u in lo uo
rt => thumb_map("nas", "naslock", "func", "sp", "lalt", "bksp"),
# d n e s w
l1 => finger_map("home", "up", "right", "down", "left"),
l2 => finger_map(undef, "f6", undef, "f5", undef),
l3 => finger_map(undef, "f4", "numlock", "f3", "esc"),
l4 => finger_map(undef, "f2", "scrolllock", "f1", "del"),
# d dd u in lo uo
lt => thumb_map("lshift", "capslock", "norm", "ret", "lctrl", "tab")
);
my(%game_key_map) = (
# d n e s w
r1 => finger_map("h", "g", "'", "m", "d"),
r2 => finger_map("t", "w", "`", "c", "f"),
r3 => finger_map("n", "v", undef, "r", "b"),
r4 => finger_map("s", "z", "\\", "l", ")"),
# d dd u in lo uo
rt => thumb_map("nas", "naslock", "func", "sp", "lalt", "bksp"),
# d n e s w
l1 => finger_map("u", "g", "i", "home", "\""),
l2 => finger_map("e", "e", "y", "c", "`"),
l3 => finger_map("o", "q", "x", "k", "esc"),
l4 => finger_map("a", "/", "[", ";", "del"),
# d dd u in lo uo
lt => thumb_map("lshift", "capslock", "game", "sp", "game_mod_2", "game_mod_1")
);
my(%game_hold_key_map) = (
# d n e s w
r1 => finger_map(undef, undef, undef, undef, undef),
r2 => finger_map(undef, undef, undef, undef, undef),
r3 => finger_map(undef, undef, undef, undef, undef),
r4 => finger_map(undef, undef, undef, undef, undef),
# d dd u in lo uo
rt => thumb_map(undef, undef, undef, undef, undef, undef),
# d n e s w
l1 => finger_map(undef, undef, undef, "ctrlv", undef),
l2 => finger_map(undef, undef, undef, "ctrlc", undef),
l3 => finger_map(undef, undef, undef, "ctrlx", undef),
l4 => finger_map("norm", undef, undef, "game", undef),
# d dd u in lo uo
lt => thumb_map(undef, undef, "game", undef, undef, undef)
);
my(%game_mod_1_key_map) = (
# d n e s w
r1 => finger_map("h", "g", "'", "m", "d"),
r2 => finger_map("t", "w", "`", "c", "f"),
r3 => finger_map("n", "v", undef, "r", "b"),
r4 => finger_map("s", "z", "\\", "l", ")"),
# d dd u in lo uo
rt => thumb_map("nas", "naslock", "func", "sp", "lalt", "bksp"),
# d n e s w
l1 => finger_map("4", "f4", "5", "9", "f8"),
l2 => finger_map("3", "f3", "f11", "8", "f7"),
l3 => finger_map("2", "f2", "f10", "7", "f6"),
l4 => finger_map("1", "f1", "f9", "6", "f5"),
# d dd u in lo uo
lt => thumb_map("lshift", "capslock", "game", "sp", "lctrl", "game_mod_1")
);
my(%game_mod_2_key_map) = (
# d n e s w
r1 => finger_map("h", "g", "'", "m", "d"),
r2 => finger_map("t", "w", "`", "c", "f"),
r3 => finger_map("n", "v", undef, "r", "b"),
r4 => finger_map("s", "z", "\\", "l", ")"),
# d dd u in lo uo
rt => thumb_map("nas", "naslock", "func", "sp", "lalt", "bksp"),
# d n e s w
l1 => finger_map("f4", "g", "'", "m", "d"),
l2 => finger_map("f3", "w", "`", "c", "f"),
l3 => finger_map("f2", "v", "kp1", "r", "b"),
l4 => finger_map("f1", "z", "\\", "l", ")"),
# d dd u in lo uo
lt => thumb_map("lshift", "capslock", "game", "sp", "game_mod_2", "game_mod_1")
);
my(%game_mod_3_key_map) = (
# d n e s w
r1 => finger_map("h", "g", "'", "m", "d"),
r2 => finger_map("t", "w", "`", "c", "f"),
r3 => finger_map("n", "v", undef, "r", "b"),
r4 => finger_map("s", "z", "\\", "l", ")"),
# d dd u in lo uo
rt => thumb_map("nas", "naslock", "func", "sp", "lalt", "bksp"),
# d n e s w
l1 => finger_map("kp1", "kp2", "kp3", "kp4", "kp5"),
l2 => finger_map("kp6", "kp7", "kp8", "kp9", "kp0"),
l3 => finger_map("kp.", "kp/", "kp*", "kp-", "kp+"),
l4 => finger_map("kp=", "left", "right", "down", "up"),
# d dd u in lo uo
lt => thumb_map("game_mod_1", "capslock", "game", "ret", "game_mod_2", "game_mod_3")
);
my(%key_maps) = (
"normal" => \%normal_key_map,
"normal_hold" => \%normal_hold_key_map,
"nas" => \%nas_key_map,
"func" => \%func_key_map,
"game" => \%game_key_map,
"game_hold" => \%game_hold_key_map,
"game_mod_1" => \%game_mod_1_key_map,
"game_mod_2" => \%game_mod_2_key_map,
"game_mod_3" => \%game_mod_3_key_map
);
generate_key_maps(\%key_maps);