-
Notifications
You must be signed in to change notification settings - Fork 4
/
actions.pm
1041 lines (853 loc) · 31.7 KB
/
actions.pm
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
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
use strict;
sub finger_map {
return {
down=>shift,
north=>shift,
east=>shift,
south=>shift,
west=>shift
};
}
sub thumb_map {
return {
down => shift,
down_down => shift,
up => shift,
inside => shift,
lower_outside => shift,
upper_outside => shift
}
}
sub press_table_label {
my($name) = shift;
return "press_table_$name";
}
#maps a button index to it's corresponding finger+direction
my(@index_map) = (
#selector 0x00
["r1", "west"], #0x00
["r1", "north"], #0x01
["l4", "west"], #0x02
["l4", "north"], #0x03
#selector 0x01
["r1", "down"], #0x04
["r1", "east"], #0x05
["l4", "down"], #0x06
["l4", "east"], #0x07
#selector 0x02
["r1", "south"], #0x08
["r2", "south"], #0x09
["l4", "south"], #0x0a
["l3", "south"], #0x0b
#selector 0x03
["r2", "west"], #0x0c
["r2", "north"], #0x0d
["l3", "west"], #0x0e
["l3", "north"], #0x0f
#selector 0x04
["r2", "down"], #0x10
["r2", "east"], #0x11
["l3", "down"], #0x12
["l3", "east"], #0x13
#selector 0x05
["r3", "west"], #0x14
["r3", "north"], #0x15
["l2", "west"], #0x16
["l2", "north"], #0x17
#selector 0x06
["r3", "down"], #0x18
["r3", "east"], #0x19
["l2", "down"], #0x1a
["l2", "east"], #0x1b
#selector 0x07
["r3", "south"], #0x1c
["r4", "south"], #0x1d
["l2", "south"], #0x1e
["l1", "south"], #0x1f
#selector 0x08
["r4", "west"], #0x20
["r4", "north"], #0x21
["l1", "west"], #0x22
["l1", "north"], #0x23
#selector 0x09
["r4", "down"], #0x24
["r4", "east"], #0x25
["l1", "down"], #0x26
["l1", "east"], #0x27
#selector 0x0a
["rt", "lower_outside"], #0x28
["rt", "upper_outside"], #0x29
["lt", "lower_outside"], #0x2a
["lt", "upper_outside"], #0x2b
#selector 0x0b
["rt", "down"], #0x2c
["rt", "down_down"], #0x2d
["lt", "down"], #0x2e
["lt", "down_down"], #0x2f
#selector 0x0c
["rt", "inside"], #0x30
["rt", "up"], #0x31
["lt", "inside"], #0x32
["lt", "up"] #0x33
);
#maps an action name to a sub that can generate the press and release code for that action
my(%action_map);
{
#generate actions for a-z and A-Z
for (my($i)=ord("a"); $i<=ord("z"); $i++) {
$action_map{chr($i)} = simple_keycode($i - ord("a") + 0x04);
$action_map{uc(chr($i))} = modified_keycode($i - ord("a") + 0x04, LSHIFT_OFFSET);
}
#generate actions for 1-9
for (my($i)=ord("1"); $i<=ord("9"); $i++) {
$action_map{chr($i)} = simple_keycode($i - ord("1") + 0x1e);
}
#0 comes before 1 in ascii, but after 9 in usb's keycodes
$action_map{"0"} = simple_keycode(0x27);
$action_map{"!"} = modified_keycode(0x1e, LSHIFT_OFFSET);
$action_map{"@"} = modified_keycode(0x1f, LSHIFT_OFFSET);
$action_map{"#"} = modified_keycode(0x20, LSHIFT_OFFSET);
$action_map{"\$"} = modified_keycode(0x21, LSHIFT_OFFSET);
$action_map{"%"} = modified_keycode(0x22, LSHIFT_OFFSET);
$action_map{"^"} = modified_keycode(0x23, LSHIFT_OFFSET);
$action_map{"&"} = modified_keycode(0x24, LSHIFT_OFFSET);
$action_map{"*"} = modified_keycode(0x25, LSHIFT_OFFSET);
$action_map{"("} = modified_keycode(0x26, LSHIFT_OFFSET);
$action_map{")"} = modified_keycode(0x27, LSHIFT_OFFSET);
$action_map{"ret"} = simple_keycode(0x28);
$action_map{"esc"} = simple_keycode(0x29);
$action_map{"bksp"} = simple_keycode(0x2a);
$action_map{"tab"} = simple_keycode(0x2b);
$action_map{"sp"} = simple_keycode(0x2c);
$action_map{"-"} = simple_keycode(0x2d);
$action_map{"_"} = modified_keycode(0x2d, LSHIFT_OFFSET);
$action_map{"="} = simple_keycode(0x2e);
$action_map{"+"} = modified_keycode(0x2e, LSHIFT_OFFSET);
$action_map{"["} = simple_keycode(0x2f);
$action_map{"{"} = modified_keycode(0x2f, LSHIFT_OFFSET);
$action_map{"]"} = simple_keycode(0x30);
$action_map{"}"} = modified_keycode(0x30, LSHIFT_OFFSET);
$action_map{"\\"} = simple_keycode(0x31);
$action_map{"|"} = modified_keycode(0x31, LSHIFT_OFFSET);
$action_map{";"} = simple_keycode(0x33);
$action_map{":"} = modified_keycode(0x33, LSHIFT_OFFSET);
$action_map{"'"} = simple_keycode(0x34);
$action_map{"\""} = modified_keycode(0x34, LSHIFT_OFFSET);
$action_map{"`"} = simple_keycode(0x35);
$action_map{"~"} = modified_keycode(0x35, LSHIFT_OFFSET);
$action_map{","} = simple_keycode(0x36);
$action_map{"<"} = modified_keycode(0x36, LSHIFT_OFFSET);
$action_map{"."} = simple_keycode(0x37);
$action_map{">"} = modified_keycode(0x37, LSHIFT_OFFSET);
$action_map{"/"} = simple_keycode(0x38);
$action_map{"?"} = modified_keycode(0x38, LSHIFT_OFFSET);
$action_map{"capslock"} = simple_keycode(0x39);
#generate actions for f1-f12
for(my($i)=1; $i<=12; $i++) {
$action_map{"f$i"} = simple_keycode(0x3A + $i - 1);
}
$action_map{"printscreen"} = simple_keycode(0x46);
$action_map{"scrolllock"} = simple_keycode(0x47);
$action_map{"pause"} = simple_keycode(0x48);
$action_map{"ins"} = simple_keycode(0x49);
$action_map{"home"} = simple_keycode(0x4a);
$action_map{"pgup"} = simple_keycode(0x4b);
$action_map{"del"} = simple_keycode(0x4c);
$action_map{"end"} = simple_keycode(0x4d);
$action_map{"pgdn"} = simple_keycode(0x4e);
$action_map{"right"} = simple_keycode(0x4f);
$action_map{"left"} = simple_keycode(0x50);
$action_map{"down"} = simple_keycode(0x51);
$action_map{"up"} = simple_keycode(0x52);
$action_map{"numlock"} = simple_keycode(0x53);
$action_map{"kp/"} = simple_keycode(0x54);
$action_map{"kp*"} = simple_keycode(0x55);
$action_map{"kp-"} = simple_keycode(0x56);
$action_map{"kp+"} = simple_keycode(0x57);
#generate actions for for keypad 1-9
for (my($i)=1; $i<=9; $i++) {
$action_map{"kp$i"} = simple_keycode(0x59 + $i-1);
}
$action_map{"kp0"} = simple_keycode(0x62);
$action_map{"kp."} = simple_keycode(0x63);
$action_map{"menu"} = simple_keycode(0x65);
$action_map{"kp="} = simple_keycode(0x67);
#generate actions for f13-f24
for(my($i)=0; $i<12; $i++) {
my($fnum) = $i+13;
$action_map{"f$fnum"} = simple_keycode(0x68 + $i);
}
$action_map{"lctrl"} = modifier_keycode(0xe0);
$action_map{"lshift"} = modifier_keycode(0xe1);
$action_map{"lalt"} = modifier_keycode(0xe2);
$action_map{"lgui"} = modifier_keycode(0xe3);
$action_map{"rctrl"} = modifier_keycode(0xe4);
$action_map{"rshift"} = modifier_keycode(0xe5);
$action_map{"ralt"} = modifier_keycode(0xe6);
$action_map{"rgui"} = modifier_keycode(0xe7);
$action_map{"nas"} = temporary_mode_action("nas", INVERSE_MASK(LED_NAS));
$action_map{"naslock"} = persistent_mode_action("nas", INVERSE_MASK(LED_NAS));
$action_map{"func"} = persistent_mode_action("func", INVERSE_MASK(LED_FUNC));
$action_map{"norm"} = temporary_mode_action("normal_hold", INVERSE_MASK(LED_NORMAL) & INVERSE_MASK(LED_10K) , "normal", INVERSE_MASK(LED_NORMAL));
$action_map{"game"} = temporary_mode_action("game_hold", INVERSE_MASK(LED_MOUSE) & INVERSE_MASK(LED_10K), "game", INVERSE_MASK(LED_MOUSE));
$action_map{"game_mod_1"} = temporary_mode_action("game_mod_1",INVERSE_MASK(LED_MOUSE) & INVERSE_MASK(LED_10K) & INVERSE_MASK(LED_NAS));
$action_map{"game_mod_2"} = temporary_mode_action("game_mod_2",INVERSE_MASK(LED_MOUSE) & INVERSE_MASK(LED_10K) & INVERSE_MASK(LED_NORMAL));
$action_map{"game_mod_3"} = temporary_mode_action("game_mod_3",INVERSE_MASK(LED_MOUSE) & INVERSE_MASK(LED_10K) & INVERSE_MASK(LED_FUNC));
$action_map{"ctrlx"} = modified_keycode(0x1b, LCTRL_OFFSET);
$action_map{"ctrlc"} = modified_keycode(0x06, LCTRL_OFFSET);
$action_map{"ctrlv"} = modified_keycode(0x19, LCTRL_OFFSET);
}
sub generate_key_maps {
my($key_maps) = shift;
foreach my $key_map_name (keys(%{$key_maps})) {
my($key_map) = $key_maps->{$key_map_name};
my($undefined_action_sub) = undefined_action();
#iterate over each physical button, and lookup and emit the code for the
#press and release actions for each
my(@press_actions);
my(@release_actions);
for (my($i)=0; $i<0x34; $i++) {
#get the finger+direction combination for this button index
my($index_map_item) = $index_map[$i];
my($finger_name) = $index_map_item->[0];
my($finger_dir) = $index_map_item->[1];
#get the direction map for a specific finger
my($finger_map) = $key_map->{$finger_name};
die "couldn't find map for finger $finger_name" unless (defined($finger_map));
#get the name of the action associated with this particular button
my($action_name) = $finger_map->{$finger_dir};
if (!defined($action_name)) {
my($labels) = &{$undefined_action_sub}($i);
push @press_actions, $labels->[BUTTON_PRESS];
push @release_actions, $labels->[BUTTON_RELEASE];
next;
}
#now look up the action
my($action) = $action_map{$action_name};
if (!defined($action)) {
die "invalid action - $action_name";
}
#this will emit the code for the press and release action
#and then we save the names in the two arrays, so we can emit a jump table afterwards
my($actions) = &$action($i);
push @press_actions, $actions->[BUTTON_PRESS];
push @release_actions, $actions->[BUTTON_RELEASE];
}
#now emit the jump table for press actions
emit_sub press_table_label($key_map_name), sub {
for (my($i)=0; $i<0x34; $i++) {
my($action_label) = $press_actions[$i];
emit ".word pm($action_label)\n";
}
};
}
}
#adds a keycode to the hid report and sends it
#r16 should contain the keycode to send
emit_sub "send_keycode_press", sub {
#find the first 0 in the button array in current_report, and store the new keycode there
_ldi zl, lo8("current_report");
_ldi zh, hi8("current_report");
_ldi r24, KEY_ARRAY_OFFSET;
_lds r25, "key_array_length";
#calculate the first address
_add zl, r24;
_adc zh, r15_zero;
#calculate the last address (we only care about the last byte)
_mov r24, zl;
_add r24, r25;
#TODO: we need to handle duplicate keys. e.g. if two buttons are pressed
#and one is a shifted variant of the other
block {
_ld r17, "z+";
_cp r17, r15_zero;
block {
_breq block_end;
#have we reached the end?
_cp r24, zl;
_breq block_end parent;
_rjmp block_begin parent;
};
_st "-z", r16;
_rjmp "send_hid_report";
};
#couldn't find an available slot in the hid report - just return
#TODO: should report ErrorRollOver in all fields
_ret;
};
#sends a simple, non-modified key release
#r16 should contain the keycode to release
emit_sub "send_keycode_release", sub {
#find the keycode in button array in current_report, and zero it out
_ldi zl, lo8("current_report");
_ldi zh, hi8("current_report");
_ldi r24, KEY_ARRAY_OFFSET;
_lds r25, "key_array_length";
#calculate the first address
_add zl, r24;
_adc zh, r15_zero;
#calculate the last address (we only care about the last byte)
_mov r24, zl;
_add r24, r25;
block {
_ld r17, "z+";
_cp r16, r17;
block {
_breq block_end;
#have we reached the end?
_cp r24, zl;
_breq block_end parent;
_rjmp block_begin parent;
};
_st "-z", r15_zero;
_rjmp "send_hid_report";
};
#huh? couldn't find the keycode in the hid report. just return
_ret;
};
#send a modifier key press
#r16 should contain a mask that specifies which modifier should be sent
#the mask should use the same bit ordering as the modifier byte in the
#hid report
emit_sub "send_modifier_press", sub {
#first, check if the modifier key is already pressed
block {
#grab the modifier byte from the hid report and check if the modifier is already pressed
_lds r17, "current_report";
_mov r18, r17;
_and r17, r16;
_brne block_end;
#set the modifier bit and store it
_or r18, r16;
_sts "current_report", r18;
_rjmp "send_hid_report";
};
_ret;
};
#send a modifier key release
#r16 should contain a mask that specifies which modifier should be sent
#the mask should use the same bit ordering as the modifier byte in the
#hid report
emit_sub "send_modifier_release", sub {
block {
#check if the modifier is actually pressed
_lds r17, "current_report";
_mov r18, r17;
_and r17, r16;
_breq block_end;
#clear the modifier bit
_com r16;
_and r18, r16;
_sts "current_report", r18;
_rjmp "send_hid_report";
};
_ret;
};
#sends current_report as an hid report
emit_sub "send_hid_report", sub {
#now, we need to send the hid report
SELECT_EP r17, EP_1;
#wait up to ~.5s until the endpoint is ready. If it takes longer than that,
#we just bail out
_clr r24;
_clr r25;
_ldi r26, 0xf3;
block {
block {
_adiw r24, 1;
_adc r26, r15_zero;
_brne block_end;
_ret;
};
_lds r17, UEINTX;
_sbrs r17, RWAL;
_rjmp block_begin;
};
#reset the hid idle period
_lds r3, "hid_idle_period";
_sts "hid_idle_ms_remaining", r3;
_lds r3, "hid_idle_period + 1";
_sts "hid_idle_ms_remaining + 1", r3;
_ldi zl, lo8("current_report");
_ldi zh, hi8("current_report");
_ldi r17, KEY_ARRAY_OFFSET;
_lds r24, "key_array_length";
_add r17, r24;
block {
_ld r18, "z+";
_sts UEDATX, r18;
_dec r17;
_brne block_begin;
};
_lds r17, UEINTX;
_cbr r17, MASK(FIFOCON);
_sts UEINTX, r17;
_ret;
};
sub simple_keycode {
my($keycode) = shift;
my($emitted) = 0;
my($labels);
return sub {
if (!$emitted) {
my($press_label) = unique_label("simple_press_action");
my($release_label) = unique_label("simple_release_action");
emit_sub $press_label, sub {
_ldi r16, $keycode;
_ldi r17, lo8(pm($release_label));
_ldi r18, hi8(pm($release_label));
_jmp "handle_simple_press";
};
emit_sub $release_label, sub {
_ldi r16, $keycode;
_jmp "send_keycode_release";
};
$labels = [$release_label, $press_label];
$emitted = 1;
}
return $labels;
}
}
#handle the press of a simple (non-modified) key
#r16 the keycode to send
#r17:r18 the address for the release routine
#y the location in the release table to store the release pointer
emit_sub "handle_simple_press", sub {
block {
#update the release table
_st "y+", r17;
_st "y", r18;
#we need to check if a purely virtual modifier key is being pressed
#if so, we need to release the virtual modifier before sending the keycode
#grab the modifier byte from the hid report
_lds r17, "current_report";
#and also grab the physical status
_lds r18, "modifier_physical_status";
#check if there are any bits that are 1 in the hid report, but 0 in the physical status
_com r18;
_and r18, r17;
#if not, we don't need to clear any virtual keys, and can proceed to send the actual key press
_breq block_end;
#otherwise, we need to clear the virtual modifiers and send a report
_com r18;
_and r17, r18;
_sts "current_report", r17;
_call "send_hid_report";
};
_rjmp "send_keycode_press";
};
sub modified_keycode {
my($keycode) = shift;
my($modifier_offset) = shift;
my($emitted) = 0;
my($labels);
return sub {
if (!$emitted) {
my($press_label) = unique_label("modified_press_action");
my($release_label) = unique_label("modified_release_action");
emit_sub $press_label, sub {
_ldi r16, $keycode;
_ldi r17, lo8(pm($release_label));
_ldi r18, hi8(pm($release_label));
_ldi r19, $modifier_offset;
_ldi r20, MASK($modifier_offset);
_jmp "handle_modified_press";
};
emit_sub $release_label, sub {
_ldi r16, $keycode;
_ldi r17, $modifier_offset;
_ldi r18, MASK($modifier_offset);
_jmp "handle_modified_release";
};
$labels = [$release_label, $press_label];
$emitted = 1;
}
return $labels;
}
}
#handle the press of a modified key
#r16 the keycode to send
#r17:r18 the address for the release routine
#r19 the offset of the modifier to use
#r20 the mask of the modifier to use
#y the location in the release table to store the release pointer
emit_sub "handle_modified_press", sub {
#save off the keycode to send
_mov r10, r16;
block {
#update the release table
_st "y+", r17;
_st "y", r18;
#increment the virtual press counter for this modifier
_ldi zl, lo8("modifier_virtual_count");
_ldi zh, hi8("modifier_virtual_count");
_add zl, r19;
_adc zh, r15_zero;
_ld r21, "z";
_inc r21;
_st "z", r21;
_mov r16, r20;
_call "send_modifier_press";
#we need to check if any other purely virtual modifier keys are being pressed
#if so, we need to release them before sending the keycode
#grab the modifier byte from the hid report
_lds r17, "current_report";
#and also grab the physical status
_lds r18, "modifier_physical_status";
#and set the bit for the modifier we just sent
_or r18, r16;
#check if there are any bits that are 1 in the hid report, but 0 in the physical status
_com r18;
_and r18, r17;
#if not, we don't need to clear any virtual keys, and can proceed to send the actual key press
_breq block_end;
#otherwise, we need to clear the virtual modifiers and send a report
_com r18;
_and r17, r18;
_sts "current_report", r17;
_call "send_hid_report";
};
_mov r16, r10;
_rjmp "send_keycode_press";
};
#handle the release of a modified key
#r16 the keycode to send
#r17 the offset of the modifier to use
#r18 the mask of the modifier to use
emit_sub "handle_modified_release", sub {
#save off r17 and r18
_mov r10, r17;
_mov r11, r18;
_call "send_keycode_release";
#decrement the virtual press counter for the modifier
_ldi zl, lo8("modifier_virtual_count");
_ldi zh, hi8("modifier_virtual_count");
_add zl, r10;
_adc zh, r15_zero;
_ld r16, "z";
_dec r16;
_st "z", r16;
block {
#we need to release the modifier key when (all of):
#1. The modifier virtual count is 0 (after decrementing for this release)
#2. The physical status for the modifier is 0
#3. The modifier in the hid report is shown as being pressed (checked in send_modifier_release)
#check if the modifier virtual count is 0 (after decrement)
_cpi r16, 0;
_brne block_end;
#check the physical flag
_lds r17, "modifier_physical_status";
_and r17, r11;
_brne block_end;
_mov r16, r11;
_jmp "send_modifier_release";
};
_ret;
};
sub modifier_keycode {
my($keycode) = shift;
my($modifier_offset) = $keycode - 0xe0;
my($emitted) = 0;
my($labels);
return sub {
if (!$emitted) {
my($press_label) = unique_label("modifier_press_action");
my($release_label) = unique_label("modifier_release_action");
emit_sub $press_label, sub {
_ldi r16, MASK($modifier_offset);
_ldi r17, lo8(pm($release_label));
_ldi r18, hi8(pm($release_label));
_jmp "handle_modifier_press";
};
emit_sub $release_label, sub {
_ldi r16, $modifier_offset;
_ldi r17, MASK($modifier_offset);
_jmp "handle_modifier_release";
};
$labels = [$release_label, $press_label];
$emitted = 1;
}
return $labels;
}
}
#handle the press of a modifier key
#r16 the mask of the modifier to send
#r17:r18 the address for the release routine
#y the location in the release table to store the release pointer
emit_sub "handle_modifier_press", sub {
#update the release table
_st "y+", r17;
_st "y", r18;
#set the bit in the modifier_physical_status byte
_lds r17, "modifier_physical_status";
_or r17, r16;
_sts "modifier_physical_status", r17;
_jmp "send_modifier_press";
};
#handle the release of a modifier key
#r16 the offset of the modifier to release
#r17 the mask of the modifier to release
emit_sub "handle_modifier_release", sub {
block {
#clear the bit in the modifier_physical_status byte
_lds r18, "modifier_physical_status";
_mov r19, r17;
_com r19;
_and r18, r19;
_sts "modifier_physical_status", r18;
#don't release the modifier if it's virtual count is still > 0
_ldi zl, lo8("modifier_virtual_count");
_ldi zh, hi8("modifier_virtual_count");
_add zl, r16;
_adc zh, r15_zero;
_ld r18, "z";
_cpi r18, 0;
_brne block_end;
_mov r16, r17;
_jmp "send_modifier_release";
};
_ret;
};
sub temporary_mode_action {
#this is the temporary mode that will be in effect only while this key is pressed
my($mode) = shift;
my($mode_led_mask) = shift;
#we can optionally change the persistent mode - that is, the mode that will become
#active once the key for the temporary mode is released
#This is used to implement, for example, the extra mode used when holding down the
#normal mode key, for left-hand ctrl+c,v,z shortcuts
my($persistent_mode) = shift;
my($persistent_mode_led_mask) = shift;
my($emitted) = 0;
my($labels);
return sub {
if (!$emitted) {
my($press_label) = unique_label("temporary_mode_press_action");
my($release_label) = unique_label("temporary_mode_release_action");
emit_sub $press_label, sub {
_ldi r16, lo8(press_table_label($mode));
_ldi r17, hi8(press_table_label($mode));
_ldi r18, lo8(pm($release_label));
_ldi r19, hi8(pm($release_label));
_ldi r22, $mode_led_mask;
if ($persistent_mode) {
_ldi r20, lo8(press_table_label($persistent_mode));
_ldi r21, hi8(press_table_label($persistent_mode));
_ldi r23, $persistent_mode_led_mask;
_rjmp "handle_temporary_persistent_mode_press";
} else {
_rjmp "handle_temporary_mode_press";
}
};
emit_sub $release_label, sub {
_ldi r16, lo8(press_table_label($mode));
_ldi r17, hi8(press_table_label($mode));
_rjmp "handle_temporary_mode_release";
};
$labels = [$release_label, $press_label];
$emitted = 1;
}
return $labels;
};
}
#handle the press of a temporary mode key that also updates the persistent mode
#r16:r17 the address of the temporary mode's press table
#r18:r19 the address for the release routine
#r20:r21 the address of the persistent mode's press table
#r22 the led mask for the temporary mode (0s indicate a lighted LED)
#r23 the led mask for the persistent mode (0s indicate a lighted LED)
#y the location in the release table to store the release pointer
emit_sub "handle_temporary_persistent_mode_press", sub {
#update the persistent mode press table pointer
_sts "persistent_mode_press_table", r20;
_sts "persistent_mode_press_table + 1", r21;
#update the persistent mode leds
_sts "persistent_mode_leds", r23;
#intentional fall-through!
};
#handle the press of a temporary mode key
#r16:r17 the address of the temporary mode's press table
#r18:r19 the address for the release routine
#r22 the led mask for the temporary mode (0s indicate a lighted LED)
#r23 the led mask for the persistent mode (0s indicate a lighted LED)
#y the location in the release table to store the release pointer
emit_sub "handle_temporary_mode_press", sub {
#update the release table
_st "y+", r18;
_st "y", r19;
#update the current press table pointer
_sts "current_press_table", r16;
_sts "current_press_table+1", r17;
#update the LEDs
_in r16, IO(PORTC);
_ori r16, INVERSE_BYTE(HOST_LED_MASK);
#make sure we don't include any of the host controlled leds
_ori r22, HOST_LED_MASK;
_and r16, r22;
_out IO(PORTC), r16;
_ret;
};
#handle the release of a temporary mode key
#r16:r17 the address of the temporary mode's press table
emit_sub "handle_temporary_mode_release", sub {
block {
#make sure that we're still in the same temporary mode. If the mode is different
#than what we expect, don't change modes. For example, if the user presses and holds
#the nas button, and then presses and hold a different temporary mode button, and
#then releases the nas button, we don't want to switch back to the persistent mode
#while the other temporary mode button is being held
_lds r18, "current_press_table";
_cp r18, r16;
_brne block_end;
_lds r18, "current_press_table + 1";
_cp r18, r17;
_brne block_end;
#restore the press table pointer from persistent_mode_press_table
_lds r16, "persistent_mode_press_table";
_sts "current_press_table", r16;
_lds r16, "persistent_mode_press_table + 1";
_sts "current_press_table + 1", r16;
#restore the LEDs from persistent_mode_leds
_in r16, IO(PORTC);
_lds r17, "persistent_mode_leds";
_ori r16, INVERSE_BYTE(HOST_LED_MASK);
#make sure we don't include any host-controlled leds
_ori r17, HOST_LED_MASK;
_and r16, r17;
_out IO(PORTC), r16;
};
_ret;
};
sub persistent_mode_action {
#this is the persistent mode to switch to
my($mode) = shift;
my($mode_led_mask) = shift;
my($emitted) = 0;
my($labels);
return sub {
if (!$emitted) {
my($press_label) = unique_label("persistent_mode_press_action");
my($release_label) = unique_label("persistent_mode_release_action");
emit_sub $press_label, sub {
_ldi r16, lo8(press_table_label($mode));
_ldi r17, hi8(press_table_label($mode));
_ldi r18, lo8(pm($release_label));
_ldi r19, hi8(pm($release_label));
_ldi r20, $mode_led_mask;
_rjmp "handle_persistent_mode_press";
};
emit_sub $release_label, sub {
_ret;
};
$labels = [$release_label, $press_label];
$emitted = 1;
}
return $labels;
}
}
#handle the press of a persistent mode key
#r16:r17 the address of the new mode's press table
#r18:r19 the address for the release routine
#r20 the led mask for the new mode (0s indicate a lighted LED)
#y the location in the release table to store the release pointer
emit_sub "handle_persistent_mode_press", sub {
#update the release table
_st "y+", r18;
_st "y", r19;
#update the current press table pointer
_sts "current_press_table", r16;
_sts "current_press_table + 1", r17;
#update the persistent mode press table pointer
_sts "persistent_mode_press_table", r16;
_sts "persistent_mode_press_table + 1", r17;
#update the LEDs
_in r16, IO(PORTC);
_ori r16, INVERSE_BYTE(HOST_LED_MASK);
#make sure we don't include any host-controlled leds
_ori r20, HOST_LED_MASK;
_and r16, r20;
_out IO(PORTC), r16;
#store the led state in persistent_mode_leds
_sts "persistent_mode_leds", r20;
_ret;
};
sub undefined_action {
my($emitted) = 0;
my($labels);
return sub {
if (!$emitted) {
my($press_label) = unique_label("undefined_press_action");
my($release_label) = unique_label("undefined_release_action");
emit_sub $press_label, sub {
_ldi r16, lo8(pm($release_label));
_ldi r17, hi8(pm($release_label));
_rjmp "handle_undefined_press";
};
emit_sub $release_label, sub {
_ret;
};
$labels = [$release_label, $press_label];
$emitted = 1;
}
return $labels;
}
}
sub type_mem_byte {
my($mem_location) = shift;
my($emitted) = 0;
my($labels);
return sub {
if (!$emitted) {
my($press_label) = unique_label("type_mem_byte_press_action");
my($release_label) = unique_label("type_mem_byte_release_action");
emit_sub $press_label, sub {
_ldi zl, lo8($mem_location);
_ldi zh, hi8($mem_location);
_ldi r16, lo8(pm($release_label));
_ldi r17, hi8(pm($release_label));
_rjmp "handle_type_mem_byte_press";
};
emit_sub $release_label, sub {
_ret;
};
$labels = [$release_label, $press_label];
$emitted = 1;
}
return $labels;
}
}