-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathwssplitter-MODELS-CLUTTERED.asm
6916 lines (6413 loc) · 273 KB
/
wssplitter-MODELS-CLUTTERED.asm
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
title "WS281X-Splitter - WS281X segment splitter/breakout/debug for Microchip PIC"
;see also ~/Doc*s/mydev/_xmas2014/src/firmware, ~/Doc*s/ESOL-fog/src/Ren*Chipi*Firmware
;================================================================================
; File: wssplitter.asm
; Date: 8/11/2021
; Version: 0.21.10
; Author: [email protected], (c)2021 [email protected]
; Device: PIC16F15313 (midrange Microchip 8-pin PIC) or equivalent running @8 MIPS
; Peripherals used: Timer0, Timer1 (gated), Timer2, no-MSSP, EUSART, no-PWM, CLC
; Compiler: mpasmx(v5.35), NOT pic-as; NOTE: custom build line is used for source code fixups
; IDE: MPLABX v5.35 (last one to include mpasm)
; Description:
; WS281X-Splitter can be used for the following purposes:
; 1. split a single WS281X data stream into <= 4 separate segments;
; creates a virtual daisy chain of LED strings instead of using null pixels between
; 2. debugger or signal integrity checker; show 24-bit WS pixel data at end of string
; 3. timing checker; display frame rate (FPS received); alternating color is used as heartbeat
; Build instructions:
;no ?Add this line in the project properties box, pic-as Global Options -> Additional options:
;no -Wa,-a -Wl,-pPor_Vec=0h,-pIsr_Vec=4h
; - use PICKit2 or 3 or equivalent programmer (PICKit2 requires PICKitPlus for newer PICs)
; Wiring:
; RA0 = debug output (32 px WS281X):
; - first 24 px shows segment 1/2/3 quad px length (0 = 1K)
; - next 8 px = FPS (255 max), msb first
; RA1 = output segment 1
; RA2 = output segment 2
; RA3 = WS281X input stream
; - first/second/third byte = segment 1/2/3 quad pixel length
; - first segment data follows immediately
; RA4 = output segment 4; receives anything after segment 1/2/3
; RA5 = output segment 3
; TODO:
; - use PPS to set RA3 as segment 3 out and RA5 as WS input?
; - uart bootloader; ground segment 0 out to enable? auto-baud detect; verify
; - custom pixel dup/skip, enforce max brightness limit?
;================================================================================
NOLIST; reduce clutter in .LST file
;NOTE: ./Makefile += AWK, GREP
;test controller: SP108E_3E6F0D
;check nested #if/#else/#endif: grep -vn ";#" this-file | grep -e "#if" -e "#else" -e "#endif"
;or: sed 's/;.*//' < ~/MP*/ws*/wssplitter.asm | grep -n -e " if " -e " else" -e " end" -e " macro" -e " while "
;grep -viE '^ +((M|[0-9]+) +)?(EXPAND|EXITM|LIST)([ ;_]|$$)' ./build/${ConfName}/${IMAGE_TYPE}/wssplitter.o.lst > wssplitter.LST
EXPAND; show macro expansions
#ifndef HOIST
#define HOIST 0
#include __FILE__; self
messg no hoist, app config/defs @47
LIST_PUSH TRUE
EXPAND_PUSH FALSE
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;//compile-time options:
;#define BITBANG; //dev/test only
;;#define SPI_TEST
#define WANT_DEBUG; //DEV/TEST ONLY!
;#define WANT_ISR; //ISR not used; uncomment to reserve space for ISR (or jump to)
#define WSBIT_FREQ (800 KHz); //WS281X "high" speed
#define WSLATCH (50 -20 usec); //end-of-frame latch time; "cheat" by using shorter interval and use the extra time for processing overhead
;#define MAX_THREADS 2; //anim xmit or frame rcv, breakout xmit
#define FOSC_FREQ (32 MHz); //max speed; NOTE: SPI 3x requires max speed, otherwise lower speed might work
;//pin assignments:
#define WSDI RA3; //RA3 = WS input stream (from controller or previous WS281X pixels)
#define BREAKOUT RA0; //RA0 = WS breakout pixels, or simple LED for dev/debug
#define LEDOUT IIFDEBUG(SEG4OUT, -1); //RA5 = simple LED output; ONLY FOR DEV/DEBUG
;#define WSCLK 4-2; //RA4 = WS input clock (recovered from WS input data signal); EUSART sync rcv clock needs a real I/O pin?
#define SEG1OUT RA1; //RA1 = WS output segment 1
#define SEG2OUT RA2; //RA2 = WS output segment 2
#define SEG3OUT RA#v(3+2); //RA5 = WS output segment 3; RA3 is input-only, use alternate pin for segment 3
#define SEG4OUT RA4; //RA4 = WS output segment 4
;#define RGSWAP 0x321; //3 = R, 2 = G, 1 = B; default = 0x321 = RGB
#define RGSWAP 0x231; //3 = R, 2 = G, 1 = B; default = 0x321 = RGB
;// default test strip
;//order 0x123: RGBYMCW => BRGMCYW
;//order 0x132: RGBYMCW => RBGMYCW
;//order 0x213: RGBYMCW => BGRCMYW
;//order 0x231: RGBYMCW => RGBYMCW ==
;//order 0x312: RGBYMCW => GBRCYMW
;//order 0x321: RGBYMCW => GRBYCMW
messg [TODO] R is sending blue(3rd byte), G is sending red(first byte), B is sending green(second byte)
;test strip is GRB order
EXPAND_POP
LIST_POP
messg end of !hoist @85
#undefine HOIST; //preserve state for plumbing @eof
#else
#if HOIST == 4; //TODO hack: simplified 8-bit parallel wsplayer
messg hoist 4: HACK: 8-bit parallel wsplayer @89
LIST_PUSH TRUE
EXPAND_PUSH FALSE
;; 8-bit parallel wsplayer ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
#define PXOUT RA0
#define UNIV_LEN 1600/100; //33; //10; //<= 2x banked GPRAM (for in-memory bitmaps), else prog space
;#define RGB_ORDER 213; //0x213; //GRB (normal is 0x123 = RGB)
#define WS_ENV 235; // 2/3/5 @ 8MIPS completely meets WS2811 + WS2812 specs
;//#define WS_ENV 334; //make start pulse longer
#ifdef LEDOUT
#undefine LEDOUT
#endif
#define LEDOUT RA4
idler_pad macro which
if $ != CONTEXT_ADDR(wsbit_idler#v(which)) + which
NOPNZ CONTEXT_ADDR(wsbit_idler#v(which)) + which - $;
endif
endm
;send 1 WS data bit to each IO pin:
;bits are assumed to be in WREG
;2/3/5 is the ideal instr env @8 MIPS- conforms strictly to WS2811 *and* WS2812 timing specs
;2/3/5 env uses 30% CPU time (3 instr), leaves 70% for caller (7 instr)
;chainable- last 4 instr of env time (idle) are typically used for call/return, loop control, or other glue logic
;earlier 1-3 instr of idle env time are typically used for data prep
;heavy rendering typically must be done between outside WS env (while waiting on timer for next frame) - there's not enough time during WS env except for the most trivial rendering
; doing_init TRUE
;not needed: IO pin init does this
; mov8 LATA, LITERAL(0); //start with WS data lines low; NOTE: this is required for correct send startup
; doing_init FALSE
;ws8_sendbit_wreg macro glue_reserved
; ws8_sendbit ORG$, ORG$, NOP #v(4 - ABS(glue_reserved))
; endm
; VARIABLE IDLER = -1; tell idler which timeslot it's in
messg [TODO] use !nop instr for fill (for easier code layout bug detection)
ws8_sendbit macro idler1, idler2, idler4
ERRIF((WS_ENV != 235) || (FOSC_FREQ != 8 MIPS), [ERROR] WS envelope WS_ENV !implemented @ fosc FOSC_FREQ - use 235 @8MIPS @__LINE__)
COMF LATA, F; //bit start; CAUTION: LATA must be 0 prior (which it should be)
; ORG $+1; placeholder
; LOCAL here1 = $
CONTEXT_SAVE wsbit_idler1
;IDLER = 1
idler1; need to load WREG here if not already loaded
; nopif $ == CONTEXT_ADDR(wsbit_idler1), 1
if $ == CONTEXT_ADDR(wsbit_idler1)
NOPNZ 1
endif
MOVWF LATA; //bit data
; ORG $+2; placeholder
; LOCAL here2 = $
CONTEXT_SAVE wsbit_idler2
;IDLER = 2
idler2; CAUTION: must preserve BSR
nopif $ == CONTEXT_ADDR(wsbit_idler2), 2
CLRF LATA; //bit end
; ORG $+4; placeholder
; LOCAL here4 = $
CONTEXT_SAVE wsbit_idler4
;IDLER = 4
idler4; CAUTION: must preserve BSR
nopif $ == CONTEXT_ADDR(wsbit_idler4), 4
;IDLER = -1
CONTEXT_SAVE wsbit_after
endm
;display "engine":
;uses a "control block" to send WS data to 8 IO pins in parallel
;control block is a struct supplied by caller, contains a 24-bit value for each IO pin (24 bytes total)
;caller can have multiple control blocks, tells engine which to use (need separate code expansions due to direct addressing)
;TODO?: convert to indirect addressing and consolidate code expansion
;rendering engine updates send count, loops until all WS pixels sent
;1 or more (mask) rgb values are also updated *after* sending all px (prep for next display frame)
;this allows display commands to be "chained" into a larger display sequence
;display commands are chainable, but caller must inline first few WS bits when using custom logic betweem WS nodes
;rendering engine loops until all pixels sent (returns 1 WS node early for custom flow control)
;NOTE: caller can inline bits from first node to reclaim busy-wait time for custom setup logic
;rendering engine applies state changes while sending last node (double-buffering !needed)
;rendering engine also maintains global state:
; BITDCL WS_EOF; HAS_WSDATA; flag telling if xmit is in progress (also used for eof flag)
; b0DCL engine_loop,; internal WS bit loop to reduce code space
b0DCL engine_mask,; rgb channel update mask
b0DCL24 engine_rgb; new rgb value to update
b0DCL16 engine_count; #WS px remaining to be sent
b0DCL engine_mask_apply,; working copy; set to 0 to disable rgb updates
b0DCL engine_last_bit,; save last bit while being updated (so double-buffering !needed)
; b0DCL engine_pxbuf, :24; rgb values for each IO pin
;below are lambda-like wrappers for ws_sendbit (so mpasm sees consistent params to ws_sendbit)
;idler macros to save caller params:
;save_mask macro mask; idler2
;; if IDLER == 2
; LOCAL here = $
; mov8 engine_mask, mask
; nopif $ != here+2, here+2 - $
;; endif
; endm
;save_rgb macro newrgb; split to fit into idler2/idler4 timeslots
; LOCAL here = $
; if IDLER == 2
; mov8 BYTEOF(engine_rgb, 0), BYTEOF(newrgb, 0);
; nopif $ != here+2, here+2 - $
;; endif
;; if IDLER == 4
; else; idler 4
;; mov16 engine_rgb, newrgb
; mov8 BYTEOF(engine_rgb, 1), BYTEOF(newrgb, 1);
; mov8 BYTEOF(engine_rgb, 2), BYTEOF(newrgb, 2);
; nopif $ != here+4, here+4 - $
; endif
; endm
;save_count macro count; idler4
;; if IDLER == 4
; LOCAL here = $
; mov16 engine_count, count;
; nopif $ != here+4, here+4 - $
;; endif
; endm
;idler to (pre-)load next WS bit to send:
;load_bit macro bitnum; idler1
;; MOVIW +bitnum[FSR_send]
; if bitnum < 24
; MOVF PXBUF + bitnum, W
; else
; MOVF engine_last_bit, W; temp save last bit
; endif
; if IDLER == 2
; MOVWF engine_last_bit; use saved copy of last bit
; endif
; endm
;idler for flow control:
;loop_setup macro count
; if IDLER == 2
; mov8 engine_loop, LITERAL(count); #WS bits to send in loop
; else; idler4
; mov16 FSR1, LITERAL(FSR_send + count); #WS bits already sent
; endif
; endm
;idlers to update engine state:
;upd_count macro send_next_nop1; split to fit into idler2/idler4 timeslots
; if IDLER == 2
; DECFSZ REGLO(engine_count), F; //REGLO(count), F; //WREG, F
; INCF REGHI(engine_count), F; kludge: cancels out later DECF upper count byte
; else; idler4 timeslot
; if send_next_nop1; commit updated count and send another node
;; setbit BITPARENT(HAS_WSDATA), TRUE; assume eof
;; NOP 1
; DECFSZ REGHI(engine_count), F; //REGLO(count), F; //WREG, F
;; setbit BITPARENT(HAS_WSDATA), FALSE; not eof
; GOTO send_next_nop1; not eof
; return; NOP 1
; else; just check for eof (disable rgb update) and continue
; mov8 engine_mask_apply, engine_mask
; DECFSZ REGHI(engine_count), W; //REGLO(count), F; //WREG, F
; CLRF engine_mask_apply; postpone rgb update until eof
; endif
; endif
; endm
;bit-remove old rgb data by setting bits ON
remove_old macro bitnum; idler2
; if IDLER == 2
MOVF engine_mask_apply, W; which bits to replace
BANKSAFE dest_arg(F) IORWF PXBUF + bitnum;, F; INDF_send, F; preset new bits ON (allows XOR to turn them off again without reloading WREG)
; else; idler4; NOTE: mask already loaded
; if bitnum < 0
; NOP 2
; else
; BANKSAFE dest_arg(F) IORWF PXBUF + bitnum + 0;, F;
; BANKSAFE dest_arg(F) IORWF PXBUF + bitnum + 1;, F;
; endif
; BANKSAFE dest_arg(F) IORWF PXBUF + bitnum + 2;, F;
; BANKSAFE dest_arg(F) IORWF PXBUF + bitnum + 3;, F;
; endif
endm
;bit-merge in new rgb data by turning bits OFF
insert_new macro bitnum; idler4
; MOVF chmask, W; need to load mask for first channel
;NOTE: WREG is already loaded by previous remove_old
; if bitnum < 0
; remove_old -2; remove a couple instead of insert
; else
ifbit engine_rgb + (bitnum / 8), 7 - (bitnum % 8), FALSE, BANKSAFE dest_arg(F) XORWF PXBUF + bitnum; INDF_send
; endif
; if bitnum + 1 < 24
ifbit engine_rgb + ((bitnum + 1) / 8), 7 - ((bitnum + 1) % 8), FALSE, BANKSAFE dest_arg(F) XORWF PXBUF + bitnum + 1; INDF_send
; endif
; else; back to caller after last bit
; return;
endm
;display engine entry point(s):
;code must be expanded for each pxbuf (due to direct addressing)
messg [TODO] add pxbuf rotate / FSR option to allow fast color changes?
messg [TODO] swap every other and use 4-bit wide pxbuf instead of 8 (saves memory)
VARIABLE PXBUF;
display_engine macro pxbuf
BANKCHK LATA; caller must set BSR; makes timing uniform in here
messg [TODO] repl PXBUF with BITVAR = bitaddr * 8 + bitnum @__LINE__
PXBUF = pxbuf; kludge: pass to idlers
;#define FSR_send pxbuf0; FSR0; FSR0 dedicated to WS send; points to control block
;#define INDF_send INDF0; FSR0 dedicated to WS send; points to control block
;fall thru to send#v(24)
;start of next WS node (24 WS data bits):
;ws8_send_more_nodes: ws8_sendbit MOVIW FSR1++, upd_count ws8_send_more_nodes, upd_count ws8_send_more_nodes; expands loop above if !eof
;first 5 bits are generic and can be custom inlined in caller:
;ws8_send#v(24)bits: ws8_sendbit MOVIW +0[FSR_send], ORG$, ORG$;
ws8_send_next_using_#v(pxbuf)_nop1: NOPNZ 1; use up idler4 residue then send next node
ws8_send#v(24)bits_using_#v(pxbuf): ws8_sendbit LODW pxbuf+0, ORG$, ORG$; load_bit 0, ORG$, ORG$;
ws8_send#v(23)bits_using_#v(pxbuf): ws8_sendbit LODW pxbuf+1, ORG$, ORG$;
#if 1; allows caller to inline more, doesn't trash FSR1
ws8_send#v(22)bits_using_#v(pxbuf): ws8_sendbit LODW pxbuf+2, ORG$, ORG$;
ws8_send#v(21)bits_using_#v(pxbuf): ws8_sendbit LODW pxbuf+3, ORG$, ORG$;
ws8_send#v(20)bits_using_#v(pxbuf): ws8_sendbit LODW pxbuf+4, ORG$, ORG$;
ws8_send#v(19)bits_using_#v(pxbuf): ws8_sendbit LODW pxbuf+5, ORG$, ORG$;
ws8_send#v(18)bits_using_#v(pxbuf): ws8_sendbit LODW pxbuf+6, ORG$, ORG$;
#else; save a little code space by consolidating generic bit-sends into a loop:
;ws8_send#v(17)bits_using_#v(pxbuf): ws8_sendbit load_bit 7, EMITL ws8_send#v(22)half_bits_using_#v(pxbuf0): loop_setup 24-20+1, loop_setup 24-22+1; #bits to send (during loop below), #sent already
;ws8_send_more_bits_using_#v(pxbuf): ws8_sendbit MOVIW FSR1++, ORG$, dest_arg(W) MOVF engine_mask;
; PAGECHK ws8_send_more_bits; do this before decfsz
; DECFSZ engine_loop, F
; GOTO ws8_send_more_bits;
; MOVWF engine_mask_apply;
error obsolete @__LINE__
#endif
;update send count + disable rgb update if !eof:
;ws8_send#v(15)bits_from_#v(pxbuf0):
; ws8_sendbit load_bit 7, upd_count 0, upd_count 0
ws8_sendbit LODW pxbuf+7, ORG$+2, ORG$+4
CONTEXT_RESTORE wsbit_idler2
DECFSZ REGLO(engine_count), F; //REGLO(count), F; //WREG, F
INCF REGHI(engine_count), F; kludge: cancels out later DECF upper count byte
CONTEXT_RESTORE wsbit_idler4
mov8 engine_mask_apply, engine_mask
DECFSZ REGHI(engine_count), W; //REGLO(count), F; //WREG, F; CAUTION: don't update- need to check again at bottom of loop
CLRF engine_mask_apply; postpone rgb update until eof
CONTEXT_RESTORE wsbit_after
;remove old rgb value + insert new (controlled by mask):
;CAUTION: don't alter bits < display (else double-buffering needed)
ws8_sendbit LODW pxbuf+8, ORG$+2, ORG$+4; copy_bit 23, remove_old 0..1
CONTEXT_RESTORE wsbit_idler2
MOVF pxbuf+23, W
MOVWF engine_last_bit; save last rgb bit so it can be changed < display
CONTEXT_RESTORE wsbit_idler4
MOVF engine_mask_apply, W; which bits to replace
IORWF pxbuf+0, F; INDF_send, F; preset new bits ON (allows XOR to turn them off again without reloading WREG)
IORWF pxbuf+23, F;
NOPNZ 1
CONTEXT_RESTORE wsbit_after
ws8_sendbit LODW pxbuf+9, remove_old 1, ORG$+4; remove_old 1..5
CONTEXT_RESTORE wsbit_idler4
IORWF pxbuf+2, F;
IORWF pxbuf+3, F;
IORWF pxbuf+4, F;
IORWF pxbuf+5, F;
CONTEXT_RESTORE wsbit_after
ws8_sendbit LODW pxbuf+10, remove_old 6, ORG$+4; remove_old 6..10
CONTEXT_RESTORE wsbit_idler4
IORWF pxbuf+7, F;
IORWF pxbuf+8, F;
IORWF pxbuf+9, F;
IORWF pxbuf+10, F;
CONTEXT_RESTORE wsbit_after
ws8_sendbit LODW pxbuf+11, remove_old 11, insert_new 0; 0..1
ws8_sendbit LODW pxbuf+12, remove_old 12, insert_new 2; 2..3
ws8_sendbit LODW pxbuf+13, remove_old 13, insert_new 4; 4..5
ws8_sendbit LODW pxbuf+14, remove_old 14, insert_new 6; 6..7
ws8_sendbit LODW pxbuf+15, remove_old 15, insert_new 8; 8..9
ws8_sendbit LODW pxbuf+16, remove_old 16, insert_new 10; 10..11
ws8_sendbit LODW pxbuf+17, remove_old 17, insert_new 12; 12..13
ws8_sendbit LODW pxbuf+18, remove_old 18, insert_new 14; 14..15
ws8_sendbit LODW pxbuf+19, remove_old 19, insert_new 16; 16..17
ws8_sendbit LODW pxbuf+20, remove_old 20, insert_new 18; 18..19
ws8_sendbit LODW pxbuf+21, remove_old 21, insert_new 20; 20..21
ws8_sendbit LODW pxbuf+22, remove_old 22, insert_new 22; 22..23
;update last bit and xfr back to caller:
; ws8_sendbit load_bit 23, remove_old 23, insert_new 23; leaves 2 instr for return to caller
; ws8_sendbit load_bit 23+100, ORG$, upd_count ws8_send_next_using_#v(pxbuf)_nop1; if !eof loop to next node
ws8_sendbit LODW engine_last_bit, ORG$, ORG$+4; send saved last bit
CONTEXT_RESTORE wsbit_idler4
PAGECHK ws8_send_next_using_#v(pxbuf)_nop1; do this before decfsz
DECFSZ REGHI(engine_count), F; //REGLO(count), F; //WREG, F
; setbit BITPARENT(HAS_WSDATA), FALSE; not eof
GOTO ws8_send_next_using_#v(pxbuf)_nop1; if !eof loop to next node; CAUTION: needs extra NOP
return;
CONTEXT_RESTORE wsbit_after
; setbit BITPARENT(HAS_WSDATA), FALSE; not eof
; return;
;second-to-last bit xfr back to caller:
; ws8_sendbit MOVIW +22[FSR_send], prefetch_lastbit, wrong-return; //leaves 2 instr for next call/goto
;last bit must be inlined in caller for loop control:
; ws8_sendbit (WREG preloaded), predec_count, (loop ctl); //inlined by caller
;additional function to upd pxbuf only (pre-send):
update_only_#v(pxbuf): DROP_CONTEXT;
; mov8 engine_mask_apply, portmask; which bits to replace
; mov24 engine_rgb, newrgb;
; REPEAT LITERAL(24), remove_old pxbuf + REPEATER;
MOVF engine_mask_apply, W; which bits to replace
REPEAT LITERAL(24), BANKSAFE dest_arg(F) IORWF pxbuf + REPEATER;, F;
REPEAT LITERAL(24/2), insert_new 2 * REPEATER;
return;
endm
;generate code from template for each pxbuf:
; rgb_channel_update FSR_even, pxbuf_odd
; rgb_channel_update FSR_odd, pxbuf_even
messg [TODO] vv use LDI for sendpx args (set FSR to TOS during prev call?) - would save ~24wd/call, allow mult (masked) rgb upd to alt pxbuf @__LINE__
; call ws8_sendpx_#v(pxbuf)
; DW 12-bit count, 2x12-bit newrgb, 8-bit mask or 2x8 count, 3x8 rgb, 1x8 mask
; messg here1 @__LINE__
;send 1 or more WS pixels:
;sets up control vars and then calls rendering engine
;2 instr from previous call are reserved for glue so calls can be chained without WS data interruption
;NOTE: 0-len send only valid at start (useful for scrolling)
;once xmit started, len must be > 0 else WS data stream will stall and pixels will latch
ws8_sendpx macro pxbuf, count, newrgb, portmask; portmask, newrgb, count; 0, prep1, prep2, prep3, prep4, prep5, prep6, prep7, prep8, prep9, prep10, prep11, prep12, prep13, prep14, prep15, prep16, prep17, prep18, prep19, prep20, prep21, prep22, prep23
; ifbit BITPARENT(HAS_WSDATA), FALSE, GOTO prep_only; _#v(NUM_SENDPX); CAUTION: true case (fall-thru) must be == 2 instr
; messg #v(BANK_TRACKER), #v(BANKOF(BANK_TRACKER)), #v(BANKOF(LATA)) @__LINE__
; messg #v(ISBANKED(LATA)), #v(ISBANKED(BANK_TRACKER)), #v(BANKOF(LATA) != BANKOF(BANK_TRACKER)) @__LINE__
; messg #v(NEEDS_BANKSEL(LATA, BANK_TRACKER)) @__LINE__
ERRIF(NEEDS_BANKSEL(LATA, BANK_TRACKER), [ERROR] banksel LATA before calling (needs to be < first call) @__LINE__)
ERRIF(NEEDS_BANKSEL(pxbuf, LATA), [ERROR] pxbuf needs to be !banked or in LATA bank #v(BANKOF(LATA)) @__LINE__)
;custom glue (4 instr): point FSR to control block and check/set BSR to LATA:
; LOCAL here1 = $
; mov16 FSR_send, LITERAL(pxbuf); 3-4 instr
; BANKCHK LATA; paranoid; check for safety
; ERROF($ > here1+4, [ERROR] either banksel LATA before calling or put pxbuf in bank 0 @__LINE__); too much glue
; nopif $ < here1+4, here1+4 - $
;PXBUF = pxbuf;
;kludge: copy params while sending WS data:
;inline as few bits as possible while saving params
; ws8_sendbit MOVIW +0[FSR_send], save_rgb newrgb, save_rgb newrgb
LOCAL mask_banksel = !ISLIT(portmask) && NEEDS_BANKSEL(portmask, LATA);
LOCAL rgb_banksel = !ISLIT(newrgb) && NEEDS_BANKSEL(newrgb, LATA);
LOCAL count_banksel = !ISLIT(count) && NEEDS_BANKSEL(count, LATA);
; ERRIF(mask_banksel || rgb_banksel || count_banksel, [TODO] banksel in sendpx setup @__LINE__);
WARNIF(mask_banksel, [ERROR] portmask needs to be !banked or in LATA bank #v(BANKOF(LATA)) @__LINE__)
WARNIF(rgb_banksel, [ERROR] newrgb needs to be !banked or in LATA bank #v(BANKOF(LATA)) @__LINE__)
WARNIF(count_banksel, [ERROR] count needs to be !banked or in LATA bank #v(BANKOF(LATA)) @__LINE__)
; BANKCHK LATA;
;TODO? if !mask don't need to save newrgb
ws8_sendbit LODW pxbuf+0, ORG$+2, ORG$+4;
CONTEXT_RESTORE wsbit_idler2
mov8 engine_mask, portmask; 1-2(4) instr
idler_pad 2
CONTEXT_RESTORE wsbit_idler4
messg [TODO] vvv fix this @__LINE__
; if !ISLIT(count) && (SIZEOF(count) != 2); saves caller RAM; NOTE: use "!= 2" so caller can use larger array
; mov8 engine_count, count; 2-4(6) instr
; mov8 REGHI(engine_count), LITERAL(0);
; else
mov16 engine_count, count; 2-4(6) instr
; endif
idler_pad 4
CONTEXT_RESTORE wsbit_after
ws8_sendbit LODW pxbuf+1, ORG$+2, ORG$+4;
CONTEXT_RESTORE wsbit_idler2
mov8 engine_rgb+0, BYTEOF(newrgb, 2); 1-2(4) instr; CAUTION: LE-> BE: hi newrgb -> eng rgb[0]
idler_pad 2
CONTEXT_RESTORE wsbit_idler4
mov8 engine_rgb+1, BYTEOF(newrgb, 1); 2-4(6) instr
mov8 engine_rgb+2, BYTEOF(newrgb, 0); CAUTION: LE-> BE: lo newrgb -> eng rgb[2]
idler_pad 4
CONTEXT_RESTORE wsbit_after
; ws8_sendbit load_bit 1, ORG$+2, ORG$+4;
; LOCAL wsbit_idlers
ws8_sendbit LODW pxbuf+2, ORG$, NOPNZ 1;
INCF REGHI(engine_count), F; kludge: bump so that decfsz exits loop on time
CALL ws8_send#v(21)bits_using_#v(pxbuf);
BANK_TRACKER = LATA; ws8_send is supposed to preserve BSR; assume it does; TODO: add call/return tracking
; if !count_banksel
; if !mask_banksel
; ws8_sendbit load_bit 1, save_mask portmask, save_count count;
; ws8_sendbit load_bit 2, ORG$, CALL ws8_send#v(21)bits_using_#v(pxbuf);
; if !rgb_banksel
; ws8_sendbit load_bit 0, save_rgb newrgb, save_rgb newrgb
; if !count_banksel
; if !mask_banksel
; ws8_sendbit load_bit 1, save_mask portmask, save_count count;
; ws8_sendbit load_bit 2, ORG$, CALL ws8_send#v(21)bits_using_#v(pxbuf);
; NOP 2;
; else; mask banksel
; ws8_sendbit load_bit 1, save_count count, save_mask portmask;
; ws8_sendbit load_bit 2, save_count count, CALL ws8_send#v(21)bits_using_#v(pxbuf);
; NOP 2;
; endif
; else; count banksel
; if !mask_banksel
; ws8_sendbit load_bit 0, save_mask portmask, load_fsr#v(1) LITERAL(count);
; ws8_sendbit load_bit 0, ORG$, save_count INDF1_postinc
; ws8_sendbit load_bit 2, ORG$, CALL ws8_send#v(21)bits_using_#v(pxbuf);
; NOP 2;
; else; mask banksel
; ws8_sendbit load_bit 0, ORG$, load_fsr#v(1) LITERAL(count);
; ws8_sendbit load_bit 0, ORG$, save_count INDF1_postinc
; ws8_sendbit load_bit 0, ORG$, save_mask portmask;
; ws8_sendbit load_bit 2, ORG$, CALL ws8_send#v(21)bits_using_#v(pxbuf);
; NOP 2;
; endif
; endif
; else; banksel needed for newrgb
; if !mask_banksel
; ws8_sendbit load_bit 0, save_mask portmask, load_fsr#v(1) LITERAL(newrgb);
; ws8_sendbit load_bit 0, save_rgb INDF1_postinc, save_rgb INDF1_postinc
; if !count_banksel
; ws8_sendbit load_bit 0, ORG$, save_count count
; ws8_sendbit load_bit 2, ORG$, CALL ws8_send#v(21)bits_using_#v(pxbuf);
; else
; ws8_sendbit load_bit 0, ORG$, load_fsr#v(1) LITERAL(count);
; ws8_sendbit load_bit 0, ORG$, save_count INDF1_postinc
; ws8_sendbit load_bit 2, ORG$, CALL ws8_send#v(21)bits_using_#v(pxbuf);
; endif
; else; mask banksel
; if !count_banksel
; ws8_sendbit load_bit 0, save_count count, load_fsr#v(1) LITERAL(newrgb);
; ws8_sendbit load_bit 0, save_rgb INDF1_postinc, save_rgb INDF1_postinc
; ws8_sendbit load_bit 0, save_count count, save_mask portmask
; ws8_sendbit load_bit 2, ORG$, CALL ws8_send#v(21)bits_using_#v(pxbuf);
; else; count banksel
; ws8_sendbit load_bit 0, ORG$, load_fsr#v(1) LITERAL(count);
;
; ws8_sendbit load_bit 0, ORG$, load_fsr#v(1) LITERAL(newrgb);
; ws8_sendbit load_bit 0, save_rgb INDF1_postinc, save_rgb INDF1_postinc
; ws8_sendbit load_bit 0, ORG$, save_mask portmask
; endif
; endif
; endif
; ws8_sendbit load_bit 2, CALL ws8_send#v(22)half_bits_using_#v(pxbuf), ORG$; WRONG
endm
;allow 0 count:
;CAUTION: only works before send
ws8_firstpx macro pxbuf, count, newrgb, portmask
LOCAL start_send, no_send;
if !ISLIT(count); run-time check; NOTE: affects timing, can't be used > send started
MOVF REGLO(count), W
if SIZEOF(count) == 2; NOTE: use "== 2" so caller can use larger array
IORWF REGHI(count), W
endif
ifbit EQUALS0 FALSE, GOTO start_send; upd will be done during (after) send
else
if count != LITERAL(0); special case: update rgb only; NOTE: can only be used < send
ws8_sendpx pxbuf, count, newrgb, portmask
exitm
endif
endif
;NOTE: timing doesn't matter here if send has not started
mov8 engine_mask_apply, portmask; which bits to replace
; mov24 engine_rgb, newrgb;
mov8 engine_rgb+0, BYTEOF(newrgb, 2); 1-2(4) instr; CAUTION: LE-> BE: hi newrgb -> eng rgb[0]
mov8 engine_rgb+1, BYTEOF(newrgb, 1); 2-4(6) instr
mov8 engine_rgb+2, BYTEOF(newrgb, 0); CAUTION: LE-> BE: lo newrgb -> eng rgb[2]
CALL update_only_#v(pxbuf);
BANKCHK LATA; in case ws8_sendpx follows
if count == LITERAL(0)
exitm
endif
goto no_send; can be another 0-len send
start_send:
BANKCHK LATA; in case len check changed BSR
ws8_sendpx pxbuf, count, newrgb, portmask
no_send:
endm
;send RLE immediate: setup overhead would be call+fsr+bsr: 9 instr + 1 extra per byte read
#if 0
;display list:
(palent-count, 1..16 palents) = 48 bytes
(disp-count, palent#)... count 0 = eof
;chainable send from prog:
;setup overhead: 5+
nbDCL count_prog,;
ws_send_from_prog macro addr
mov16 FSR1, LITERAL(0x8000 | addr);
; mov8 LDI_len, len;
BANKCHK LATA
send_prog_loop: ;NOTE: each INDF access from prog space uses 1 extra instr cycle
mov8 count_prog, INDF1_postinc; length: 3 instr
mov8 rgb+0, INDF1_postinc; rgb value: 9 instr
mov8 rgb+1, INDF1_postinc;
mov8 rgb+2, INDF1_postinc;
DECFSZ count_prog, F
GOTO send_prog_loop;
; mov16 TOS, FSR1; return past immediate data
return;
endm
#endif
;pxbuf load-immediate:
LDI #v(3*8); code expansion; TODO: why is #v() needed here? mpasm gets confused without it
PBLI macro pxbuf
mov16 FSR0, LITERAL(pxbuf); destination
CALL LDI_#v(3*8);
endm
constant OFF = LITERAL(0);
constant RED = LITERAL(0x030000);
constant GREEN = LITERAL(0x000300);
constant BLUE = LITERAL(0x000003);
constant YELLOW = LITERAL(0x020200);
constant CYAN = LITERAL(0x000202);
constant MAGENTA = LITERAL(0x020002);
constant WHITE = LITERAL(0x010101);
#if 1
constant RED_FULL = LITERAL(0xFF0000);
constant GREEN_FULL = LITERAL(0x00FF00);
constant BLUE_FULL = LITERAL(0x0000FF);
constant YELLOW_HALF = LITERAL(0x7F7F00);
constant CYAN_HALF = LITERAL(0x007F7F);
constant MAGENTA_HALF = LITERAL(0x7F007F);
constant WHITE_THIRD = LITERAL(0x555555);
#else
constant RED_FULL = RED;
constant GREEN_FULL = GREEN;
constant BLUE_FULL = BLUE;
constant YELLOW_HALF = YELLOW;
constant CYAN_HALF = CYAN;
constant MAGENTA_HALF = MAGENTA;
constant WHITE_THIRD = WHITE;
#endif
b0DCL pxbuf, :24; //8 parallel 24-bit values (1 for each IO pin)
display_engine pxbuf;
; b0DCL altbuf, :24; //alternate pxbuf
; display_engine altbuf;
doing_init TRUE
PBLI pxbuf; set initial colors
DW 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0;
; PBLI altbuf; set initial colors
; DW 0x01, 0x1e, 0x66, 0xaa, 0x01, 0x1e, 0x66, 0xaa, 0x01, 0x1e, 0x66, 0xaa, 0x01, 0x1e, 0x66, 0xaa, 0x01, 0x1e, 0x66, 0xaa, 0x01, 0x1e, 0x66, 0xaa;
doing_init FALSE
constant devpanel_mask = LITERAL(0x80);
constant CHALL = LITERAL(0xFF);
constant CHNONE = LITERAL(0);
b0DCL tree_mask,; b1DCL: TODO
doing_init TRUE
mov8 tree_mask, LITERAL(0x18);
doing_init FALSE
#if 0
const pxbuf =
[
0x111111,
0x222222,
0x333333,
0x444444,
0x555555,
0x666666,
0x777777,
0x888888,
];
console.log(pivot32x8(pxbuf).map(row => hex(row)).join(", "), srcline());
function pivot32x8(buf32x8)
{
const retval = [];
for (let bit = u32(0x80000000), count = 0; bit; bit >>>= 1, ++count)
retval.push(buf32x8.reduce((colval, rowval, y) => colval | ((rowval & bit)? 1 << (8-1 - y): 0), 0));
return retval;
}
#endif
;pxbuf_init:
; DW 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0;
;altbuf_init:
; DW 0x01, 0x1e, 0x66, 0xaa, 0x01, 0x1e, 0x66, 0xaa;
; DW 0x01, 0x1e, 0x66, 0xaa, 0x01, 0x1e, 0x66, 0xaa;
; DW 0x01, 0x1e, 0x66, 0xaa, 0x01, 0x1e, 0x66, 0xaa;
#if 0
b0DCL16 ofs;
b0DCL16 rem;
doing_init TRUE; CAUTION: must be outside thread def
messg [TODO] doing_init += jump over @__LINE__
;#define DECFSZ16_BUMP 0x100; kludge: allow 1 extra dec on high byte
#undefine UNIV_LEN
#define UNIV_LEN 15; 240
mov16 rem, LITERAL(UNIV_LEN - 1); + DECFSZ16_BUMP);
doing_init FALSE
#endif
messg [TOD] add "test" function (read RA3?), also override hard-wired fx with RX data @__LINE__
THREAD_DEF ws_player, 4
ws_player: DROP_CONTEXT;
messg [TODO] vvvv add wait intv to context @__LINE__
WAIT 1 sec; give power time to settle, and set up timer0 outside player loop
; mov8 tree_mask, LITERAL(0x18);
; mov24 altbuf+0*3, LITERAL(0x11111111);
; mov24 altbuf+1*3, LITERAL(0x22222222);
; mov24 altbuf+2*3, LITERAL(0x33333333);
; mov24 altbuf+3*3, LITERAL(0x44444444);
; mov24 altbuf+4*3, LITERAL(0x55555555);
; mov24 altbuf+5*3, LITERAL(0x66666666);
; mov24 altbuf+6*3, LITERAL(0x77777777);
; mov24 altbuf+7*3, LITERAL(0x88888888);
; memset(pxbuf, 0, LITERAL(24));
; memcpy(pxbuf, 0x8000 | pxbuf_init, LITERAL(24));
; memcpy(altuf, 0x8000 | altbuf_init, LITERAL(24));
; PBLI pxbuf; set initial colors
; DW 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0;
; PBLI altbuf; set initial colors
; DW 0x01, 0x1e, 0x66, 0xaa, 0x01, 0x1e, 0x66, 0xaa, 0x01, 0x1e, 0x66, 0xaa, 0x01, 0x1e, 0x66, 0xaa, 0x01, 0x1e, 0x66, 0xaa, 0x01, 0x1e, 0x66, 0xaa;
; PBLI pxbuf; set initial colors
; constant O = 0, I = 255;
; DW O,O,O,O,O,O,I,I, O,O,O,O,O,O,O,O, O,O,O,O,O,O,O,O; //red
ws8_firstpx pxbuf, LITERAL(0), RED_FULL, CHALL;
play_loop: DROP_CONTEXT
messg [TODO] blt ops via chmask? (xor, and+or, etc) @__LINE__
messg [TODO] 8-bit anim count, 4-6 bit palette entry, use alt pxbuf to get mult color changes @__LINE__
#if 0; tree + snowflake anim
messg tree + snow anim @__LINE__
constant TREE_CH = #v(LITERAL(1)); RA0
;#define BRANCH_LEN 33
;#define NUM_BRANCH 24-1; "-1" for test only
;#define ANIM_SPEED (1 sec/10)
#define BRANCH_LEN 16; //dev panel
#define NUM_BRANCH 16-1; //dev panel; "-1" for test only
; constant FOREST_GREEN = #v(LITERAL(0x00BF00)); //RGB green 75%
; constant ICE_WHITE = #v(LITERAL(0xBFFFFF)); //RGB cyan 25%
constant FOREST_GREEN = #v(LITERAL(0x020000)); //devpanel GRB green 75%
constant ICE_WHITE = #v(LITERAL(0x010202)); //devpanel GRB cyan 25%
#define SNFLAKE_SIZE 2; TODO: adjust this
#define MAX_SNFLAKES 6; TODO: adjust this
#define NUM_BREAKS (SNFLAKE_SIZE * MAX_SNFLAKES)
;NOTE: snow can be in 2 places on each branch: flake left edge or flake right edge
;however, there can only be a limited number of snowflakes at a time
;render logic will position these breaks as needed, within or across branches
;only need enough RLE control vars for begin/end of each simultaneous snowflake
;unusued gaps can be at beginning or end
;gaps must be @beginning if exact fixed #nodes needed (props chained) but requires variable length
;collisions with snflake size > 1 also requires variable snow lengths
REPEAT LITERAL(NUM_BREAKS+1), b0DCL16 treelen_#v(REPEATER); tree/branch len must be 16-bit (tree has > 256 nodes)
REPEAT LITERAL(NUM_BREAKS), b0DCL8 flakelen_#v(REPEATER); snow/flake len is <= 3 (0 if unused, 4 during collision) so use 8-bit vars
constant RLE_FRAMELEN = 2 * (NUM_BREAKS + 1) + NUM_BREAKS;
;TODO:
; b0DCL snflake_X,:MAX_SNFLAKES; //X,Y pos controls rendering
; b0DCL snflake_Y,:MAX_SNFLAKES;
; REPEAT LITERAL(MAX_SNFLAKES), CLRF snflake_X + REPEATER;
; REPEAT LITERAL(MAX_SNFLAKES), CLRF snflake_Y + REPEATER;
mov16 FSR1, LITERAL(tree_snowflakes_anim | 0x8000); read prog space
GOTO not_eof;
tree_snowflakes_anim: //RLE-encoded bitmaps, 1 per frame, 3 * #breaks + 1 bytes each
;TODO: bit-pack to 1 treelen + 1 flakelen per prog word; compresses 3:1 (37 -> 13 words)
DW
tree_snowflakes_anim_eof: ;NOTE: next entry should match first for smooth anim loop
constant anim_size = tree_snowflakes_anim_eof - tree_snowflakes_anim;
ERRIF(anim_size % RLE_FRAMELEN, [ERROR] RLE contains incomplete entry: len #v(anim_size), entry len: #v(RLE_FRAMELEN), fragment: #v(anim_size % RLE_FRAMELEN1) @__LINE__
tree_loop: DROP_CONTEXT;
;rendering: figure out how long to draw each color
; REPEAT LITERAL(NUM_BREAKS+1), b0DCL16 treelen_#v(REPEATER); tree/branch len must be 16-bit (tree has > 256 nodes)
; REPEAT LITERAL(NUM_BREAKS), b0DCL8 flakelen_#v(REPEATER); snow/flake len is <= 3 (0 if unused, 4 during collision) so use 8-bit vars
;NOPE: too complicated :P
;instead, render with sequencing software and just play back RLE-encoded bitmaps here:
MOVF REGLO(FSR1), W;
XORLW (tree_snowflakes_eof | 0x8000) & 0xFF;
ifbit EQUALS0 FALSE, GOTO not_eof;
MOVF REGHI(FSR1), W;
XORLW ((tree_snowflakes_eof | 0x8000) >> 8) & 0xFF;
ifbit EQUALS0 FALSE, GOTO not_eof;
mov16 FSR1, LITERAL(tree_snowflakes_anim | 0x8000); rewind
not_eof: DROP_CONTEXT;
messg [TODO] generalize into RLE-decode, RLE-playback @__LINE__
mov16 FSR0, LITERAL(treelen_#v(0))
b0DCL8 rle_decode_count;
mov8 rle_decode_count, LITERAL(RLE_FRAMELEN);
rle_decode_loop: DROP_CONTEXT;
mov8 INFD0_postinc, INDF1_postinc;
DECFSZ rle_decode_count, F;
GOTO rle_decode_loop;
;draw:
ws8_firstpx pxbuf, LITERAL(0), FOREST_GREEN, TREE_CH;
variable break_num = 0
while break_num < NUM_BREAKS
ws8_sendpx pxbuf, treelen_#v(break_num), ICE_WHITE, TREE_CH;
ws8_sendpx pxbuf, flakelen_#v(break_num), FOREST_GREEN, TREE_CH;
break_num += 1
endw
ws8_sendpx pxbuf, treelen_#v(break_num), MAGENTA_FULL, TREE_CH; //one extra tree seg
ws8_sendpx pxbuf, LITERAL(1), OFF, TREE_CH; ;debug/test marker
WAIT 1 sec/10; //anim speed
GOTO tree_loop;
#endif
#if 1; angel NEEDS_REPAIR with shaped wings
#define ANGEL_CH LITERAL(0x3F & ~0x18);
#define STATUS_CH LITERAL(0x10);
;//palent[0]: 0x7f0000, #occ 266
;//palent[1]: 0x3f3f36, #occ 176
;//palent[2]: 0x0, #occ 52
;//palent[3]: 0x7f7f00, #occ 46
constant PAL0 = LITERAL(0x5f7f00);
constant PAL1 = LITERAL(0x3f3f36);
constant PAL2 = LITERAL(0);
constant PAL3 = LITERAL(0x7f7f00);
angel_loop: DROP_CONTEXT;
ws8_firstpx pxbuf, LITERAL(0), LITERAL(0x010000), STATUS_CH; heartbeat
CALL draw;
ws8_firstpx pxbuf, LITERAL(0), LITERAL(0x000100), STATUS_CH; heartbeat
CALL draw;
GOTO angel_loop;
draw: DROP_CONTEXT;
BANKCHK LATA;
ws8_firstpx pxbuf, LITERAL(0), PAL1, ANGEL_CH; set color for new frame
;//30 RLE blocks:
; RLE 154*[1], 4*[2], 16*[0], 7*[2], 20*[0], 1*[2], 23*[0], 1*[2], 23*[0], 3*[2], 20*[0],
ws8_sendpx pxbuf, LITERAL(154), PAL2, ANGEL_CH;
ws8_sendpx pxbuf, LITERAL(4), PAL0, ANGEL_CH;
ws8_sendpx pxbuf, LITERAL(16), PAL2, ANGEL_CH;
ws8_sendpx pxbuf, LITERAL(7), PAL0, ANGEL_CH;
ws8_sendpx pxbuf, LITERAL(20), PAL2, ANGEL_CH;
ws8_sendpx pxbuf, LITERAL(1), PAL0, ANGEL_CH;
ws8_sendpx pxbuf, LITERAL(23), PAL2, ANGEL_CH;
ws8_sendpx pxbuf, LITERAL(1), PAL0, ANGEL_CH;
ws8_sendpx pxbuf, LITERAL(23), PAL2, ANGEL_CH;
ws8_sendpx pxbuf, LITERAL(3), PAL0, ANGEL_CH;
ws8_sendpx pxbuf, LITERAL(20), PAL2, ANGEL_CH;
; 6*[2], 16*[0], 4*[2], 23*[3], 54*[0], 23*[3], 4*[2], 16*[0], 7*[2], 20*[0], 1*[2],
ws8_sendpx pxbuf, LITERAL(6), PAL0, ANGEL_CH;
ws8_sendpx pxbuf, LITERAL(16), PAL2, ANGEL_CH;
ws8_sendpx pxbuf, LITERAL(4), PAL3, ANGEL_CH;
ws8_sendpx pxbuf, LITERAL(23), PAL0, ANGEL_CH;
ws8_sendpx pxbuf, LITERAL(54), PAL3, ANGEL_CH;
ws8_sendpx pxbuf, LITERAL(23), PAL2, ANGEL_CH;
ws8_sendpx pxbuf, LITERAL(4), PAL0, ANGEL_CH;
ws8_sendpx pxbuf, LITERAL(16), PAL2, ANGEL_CH;
ws8_sendpx pxbuf, LITERAL(7), PAL0, ANGEL_CH;
ws8_sendpx pxbuf, LITERAL(20), PAL2, ANGEL_CH;
ws8_sendpx pxbuf, LITERAL(1), PAL0, ANGEL_CH;
; 23*[0], 1*[2], 23*[0], 3*[2], 20*[0], 6*[2], 16*[0], 4*[2]; //375..541
ws8_sendpx pxbuf, LITERAL(23), PAL2, ANGEL_CH;
ws8_sendpx pxbuf, LITERAL(1), PAL0, ANGEL_CH;
ws8_sendpx pxbuf, LITERAL(23), PAL2, ANGEL_CH;
ws8_sendpx pxbuf, LITERAL(3), PAL0, ANGEL_CH;
ws8_sendpx pxbuf, LITERAL(20), PAL2, ANGEL_CH;
ws8_sendpx pxbuf, LITERAL(6), PAL0, ANGEL_CH;
ws8_sendpx pxbuf, LITERAL(16), PAL2, ANGEL_CH;
ws8_sendpx pxbuf, LITERAL(4), OFF, ANGEL_CH;
WAIT 1 sec
return;
#endif
#if 0; 3 wisemen + angel //GRB
messg 3 wisemen + angel @__LINE__
constant WM1_CH = #v(LITERAL(1)); RA0
constant WM2_CH = #v(LITERAL(2)); RA1
constant WM3_CH = #v(LITERAL(4)); RA2
constant ANGEL_CH = #v(LITERAL(16)); RA4
constant WM1_BODY = #v(LITERAL(0x7F007F)); //GRB cyan 50%
constant WM2_BODY = #v(LITERAL(0x0000FF)); //GRB blue 100%
constant WM3_BODY = #v(LITERAL(0x007F7F)); //GRB magenta 50%
constant WM_HEAD = #v(LITERAL(0x5F7F00)); //GRB gold 50%
constant ANGEL_WINGS = #v(LITERAL(0x5F7F00)); //GRB gold 50%
constant ANGEL_BODY = #v(LITERAL(0x3F3F30)); //GRB warm white 25%
constant ANGEL_HAIR = #v(LITERAL(0x7F7F00)); //GRB yellow 50%
constant ANGEL_HALO = #v(LITERAL(0x5F7F00)); //GRB gold 50%
constant ANGEL_TRUMPET = #v(LITERAL(0x5F7F00)); //GRB gold 50%
w3a_loop: DROP_CONTEXT;
ws8_firstpx pxbuf, LITERAL(0), WM1_BODY, WM1_CH;
ws8_firstpx pxbuf, LITERAL(0), WM2_BODY, WM2_CH;
ws8_firstpx pxbuf, LITERAL(0), WM3_BODY, WM3_CH;
ws8_firstpx pxbuf, LITERAL(0), ANGEL_BODY, ANGEL_CH;
;// 11 RLE (box wings): 176*[1], 64*[3], 60*[4], 20*[2], 23*[5], 7*[7], 40*[6], 7*[7], 23*[5], 144*[2], 1,036*[0]; //0..1599
#if 0
;//28 RLE (trimmed wings):
RLE 176*[1], 4*[8], 16*[4], 7*[8], 20*[4], 1*[8], 16*[4], 7*[3], 1*[9], 46*[3], 6*[9], 20*[2], 23*[5], 7*[7], 40*[6], 7*[7]; //0..397
RLE 23*[5], 4*[0], 16*[2], 7*[0], 20*[2], 1*[0], 23*[2], 1*[0], 46*[2], 6*[0], 20*[2], 1,036*[0]; //397..1599
#endif
ws8_sendpx pxbuf, LITERAL(176), ANGEL_WINGS, ANGEL_CH;
ws8_sendpx pxbuf, LITERAL(64), WM_HEAD, WM1_CH | WM2_CH | WM3_CH;
ws8_sendpx pxbuf, LITERAL(60), ANGEL_WINGS, CHNONE; ANGEL_CH;
ws8_sendpx pxbuf, LITERAL(20), ANGEL_HAIR, ANGEL_CH;
ws8_sendpx pxbuf, LITERAL(23), ANGEL_HALO, ANGEL_CH;
ws8_sendpx pxbuf, LITERAL(7), ANGEL_TRUMPET, ANGEL_CH;
ws8_sendpx pxbuf, LITERAL(40), ANGEL_HALO, ANGEL_CH;
ws8_sendpx pxbuf, LITERAL(7), ANGEL_HAIR, ANGEL_CH;
ws8_sendpx pxbuf, LITERAL(23), ANGEL_WINGS, ANGEL_CH;
ws8_sendpx pxbuf, LITERAL(144), LITERAL(0), CHALL;
WAIT 1 sec;
; GOTO w3a_loop;
#endif
#if 0; wisemen hard-code loop
messg wisemen @__LINE__
#undefine UNIV_LEN
#define UNIV_LEN 400
#define BODY_LEN #v(LITERAL(12 * 20))
#define HEAD_LEN #v(LITERAL(18 * 4 - 6 * 2))
constant GOLD_FULL = #v(LITERAL(0xffbf00));
BANKCHK LATA;
ws8_sendpx pxbuf, BODY_LEN, GOLD_FULL, CHALL; all red
ws8_sendpx pxbuf, HEAD_LEN, GREEN_FULL, CHALL; all gold
WAIT 1 sec;/10;
BANKCHK LATA;
ws8_sendpx pxbuf, BODY_LEN, GOLD_FULL, CHALL; all green
ws8_sendpx pxbuf, HEAD_LEN, BLUE_FULL, CHALL; all gold
WAIT 1 sec;/10;
BANKCHK LATA;
ws8_sendpx pxbuf, BODY_LEN, GOLD_FULL, CHALL; all blue
ws8_sendpx pxbuf, HEAD_LEN, YELLOW_HALF, CHALL; all gold
WAIT 1 sec;/10;
BANKCHK LATA;
ws8_sendpx pxbuf, BODY_LEN, GOLD_FULL, CHALL; all yellow
ws8_sendpx pxbuf, HEAD_LEN, MAGENTA_HALF, CHALL; all gold
WAIT 1 sec;/10;
BANKCHK LATA;
ws8_sendpx pxbuf, BODY_LEN, GOLD_FULL, CHALL; all magenta
ws8_sendpx pxbuf, HEAD_LEN, CYAN_HALF, CHALL; all gold
WAIT 1 sec;/10;
BANKCHK LATA;
ws8_sendpx pxbuf, BODY_LEN, GOLD_FULL, CHALL; all cyan
ws8_sendpx pxbuf, HEAD_LEN, WHITE_THIRD, CHALL; all gold
WAIT 1 sec;/10;
BANKCHK LATA;
ws8_sendpx pxbuf, BODY_LEN, GOLD_FULL, CHALL; all white
ws8_sendpx pxbuf, HEAD_LEN, RED_FULL, CHALL; all gold
WAIT 1 sec;/10;
;;fall-thru to fence GOTO play_loop;
#endif
#if 0; fence osc between red + green 63% (0xA0/0xFF), fade for 3 sec then pause for 5 sec
messg fence red-green fade @__LINE__
;12V BRG order:
; b0DCL8 pause;
#define FENCE_CH LITERAL(0x3F & ~0x18);
#define STATUS_CH LITERAL(0x10);
b0DCL24 RED_FENCE;
b0DCL24 GREEN_FENCE;
; WAIT 1 sec/10; pre-set timer0
; GOTO not_eof;
; GOTO fence_anim_eof; skip over RLE frames in prog space
GOTO rewind;
constant RLE_FRAMELEN = 3; read 1 RGB value each frame
; UGLY_PASS12FIX +1;
fence_anim: ;//RLE-encoded bitmaps, 1 per frame; NOTE: symetrical: first half of loop == second in reverse
;TODO: bit-pack to 25 bits: 1 bit pause + 24-bit rgb color; compresses 4:1 words
DW 0xa0, 0x0, 0x0
DW 0xa0, 0xa, 0x0
DW 0xa0, 0x15, 0x0
DW 0xa0, 0x1f, 0x0
DW 0xa0, 0x2a, 0x0
DW 0xa0, 0x35, 0x0
DW 0xa0, 0x40, 0x0
DW 0xa0, 0x4a, 0x0
DW 0xa0, 0x55, 0x0
DW 0xa0, 0x60, 0x0
DW 0xa0, 0x6a, 0x0
DW 0xa0, 0x75, 0x0
DW 0xa0, 0x80, 0x0
DW 0xa0, 0x8a, 0x0
DW 0xa0, 0x95, 0x0
DW 0xa0, 0xa0, 0x0
DW 0x95, 0xa0, 0x0
DW 0x8a, 0xa0, 0x0
DW 0x7f, 0xa0, 0x0
DW 0x75, 0xa0, 0x0
DW 0x6a, 0xa0, 0x0
DW 0x60, 0xa0, 0x0
DW 0x55, 0xa0, 0x0
DW 0x4a, 0xa0, 0x0
DW 0x3f, 0xa0, 0x0
DW 0x35, 0xa0, 0x0
DW 0x2a, 0xa0, 0x0
DW 0x20, 0xa0, 0x0
DW 0x15, 0xa0, 0x0
DW 0xa, 0xa0, 0x0
;fence_anim_pause:
DW 0x0, 0xa0, 0x0
DW 0xa, 0xa0, 0x0
DW 0x15, 0xa0, 0x0
DW 0x20, 0xa0, 0x0
DW 0x2a, 0xa0, 0x0
DW 0x35, 0xa0, 0x0
DW 0x3f, 0xa0, 0x0
DW 0x4a, 0xa0, 0x0
DW 0x55, 0xa0, 0x0
DW 0x60, 0xa0, 0x0
DW 0x6a, 0xa0, 0x0
DW 0x75, 0xa0, 0x0
DW 0x7f, 0xa0, 0x0
DW 0x8a, 0xa0, 0x0
DW 0x95, 0xa0, 0x0
DW 0xa0, 0xa0, 0x0
DW 0xa0, 0x95, 0x0
DW 0xa0, 0x8a, 0x0
DW 0xa0, 0x80, 0x0