-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathGPU.ice
1061 lines (980 loc) · 42.9 KB
/
GPU.ice
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
algorithm gpu_queue(
output int11 bitmap_x_write,
output int11 bitmap_y_write,
output uint7 bitmap_colour_write,
output uint7 bitmap_colour_write_alt,
output uint1 bitmap_write,
output uint9 bitmap_crop_left,
output uint9 bitmap_crop_right,
output uint8 bitmap_crop_top,
output uint8 bitmap_crop_bottom,
output uint4 gpu_active_dithermode,
input uint9 crop_left,
input uint9 crop_right,
input uint8 crop_top,
input uint8 crop_bottom,
input int11 gpu_x,
input int11 gpu_y,
input uint7 gpu_colour,
input uint7 gpu_colour_alt,
input int11 gpu_param0,
input int11 gpu_param1,
input int11 gpu_param2,
input int11 gpu_param3,
input int11 gpu_param4,
input int11 gpu_param5,
input uint4 gpu_write,
input uint4 gpu_dithermode,
simple_dualport_bram_port0 blit1tilemap,
simple_dualport_bram_port0 characterGenerator8x8,
simple_dualport_bram_port0 colourblittilemap,
input uint7 pb_colour7,
input uint8 pb_colour8r,
input uint8 pb_colour8g,
input uint8 pb_colour8b,
input uint2 pb_newpixel,
input uint7 vector_block_colour,
input int11 vector_drawer_gpu_x,
input int11 vector_drawer_gpu_y,
input int11 vector_drawer_gpu_param0,
input int11 vector_drawer_gpu_param1,
input uint1 vector_drawer_gpu_write,
input uint1 vector_block_active,
output uint1 queue_full(0),
output uint1 queue_complete(1),
output uint1 gpu_active
) <autorun,reginputs> {
gpu GPU(
blit1tilemap <:> blit1tilemap,
characterGenerator8x8 <:> characterGenerator8x8,
colourblittilemap <:> colourblittilemap,
bitmap_x_write :> bitmap_x_write, bitmap_y_write :> bitmap_y_write,
bitmap_colour_write :> bitmap_colour_write, bitmap_colour_write_alt :> bitmap_colour_write_alt,
bitmap_write :> bitmap_write,
gpu_active_dithermode :> gpu_active_dithermode,
pb_colour7 <: pb_colour7, pb_colour8r <: pb_colour8r, pb_colour8g <: pb_colour8g, pb_colour8b <: pb_colour8b, pb_newpixel <: pb_newpixel,
gpu_active :> gpu_active
);
// QUEUE STORAGE
uint1 queue_busy = 0;
int11 queue_x = uninitialised; int11 queue_y = uninitialised;
uint7 queue_colour = uninitialised; uint7 queue_colour_alt = uninitialised;
int11 queue_param0 = uninitialised; int11 queue_param1 = uninitialised;
int11 queue_param2 = uninitialised; int11 queue_param3 = uninitialised;
int11 queue_param4 = uninitialised; int11 queue_param5 = uninitialised;
uint4 queue_dithermode = uninitialised;
uint4 queue_write = uninitialised;
uint9 queue_cropL = uninitialised; uint8 queue_cropT = uninitialised;
uint9 queue_cropR = uninitialised; uint8 queue_cropB = uninitialised;
GPU.gpu_write := 0; queue_full := vector_block_active | queue_busy ; queue_complete := ~( gpu_active | queue_full );
bitmap_crop_left := GPU.crop_left; bitmap_crop_right := GPU.crop_right; bitmap_crop_top := GPU.crop_top; bitmap_crop_bottom := GPU.crop_bottom;
always_before {
if( vector_drawer_gpu_write ) {
GPU.gpu_colour = vector_block_colour;
GPU.gpu_x = vector_drawer_gpu_x; GPU.gpu_y = vector_drawer_gpu_y;
GPU.gpu_param0 = vector_drawer_gpu_param0; GPU.gpu_param1 = vector_drawer_gpu_param1;
GPU.gpu_param2 = 1;
GPU.crop_left = crop_left; GPU.crop_right = crop_right; GPU.crop_top = crop_top; GPU.crop_bottom = crop_bottom;
GPU.gpu_write = 2;
}
if( |gpu_write ) {
queue_busy = 1; queue_dithermode = gpu_dithermode; queue_colour = gpu_colour; queue_colour_alt = gpu_colour_alt; queue_write = gpu_write;
( queue_x, queue_y ) = copycoordinates( gpu_x, gpu_y );
( queue_param0, queue_param1 ) = copycoordinates( gpu_param0, gpu_param1 );
( queue_param2, queue_param3 ) = copycoordinates( gpu_param2, gpu_param3 );
( queue_param4, queue_param5 ) = copycoordinates( gpu_param4, gpu_param5 );
( queue_cropL, queue_cropR ) = copycoordinates( crop_left, crop_right );
( queue_cropT, queue_cropB ) = copycoordinates( crop_top, crop_bottom );
}
}
always_after {
if( queue_busy & ~gpu_active ) {
GPU.gpu_dithermode = queue_dithermode; GPU.gpu_colour = queue_colour; GPU.gpu_colour_alt = queue_colour_alt;
GPU.gpu_x = queue_x; GPU.gpu_y = queue_y;
GPU.gpu_param0 = queue_param0; GPU.gpu_param1 = queue_param1;
GPU.gpu_param2 = queue_param2; GPU.gpu_param3 = queue_param3;
GPU.crop_left = queue_cropL; GPU.crop_right = queue_cropR; GPU.crop_top = queue_cropT; GPU.crop_bottom = queue_cropB;
switch( queue_write ) {
case 0: {}
case 14: {
// SECOND TRIANGLE TO GPU
GPU.gpu_param0 = queue_param4; GPU.gpu_param1 = queue_param5;
GPU.gpu_write = 6;
queue_write = 0;
}
case 15: {
// QUADRILATERAL, SEND FIRST TRIANGLE TO GPU
GPU.gpu_write = 6;
queue_write = 14;
}
default: {
// EVERYTHING ELSE, SEND TO GPU
GPU.gpu_write = queue_write;
queue_write = 0;
}
}
}
queue_busy = |queue_write;
}
}
algorithm gpu(
simple_dualport_bram_port0 blit1tilemap,
simple_dualport_bram_port0 characterGenerator8x8,
simple_dualport_bram_port0 colourblittilemap,
// GPU to SET and GET pixels
output int11 bitmap_x_write,
output int11 bitmap_y_write,
output uint7 bitmap_colour_write,
output uint7 bitmap_colour_write_alt,
output uint1 bitmap_write,
output uint4 gpu_active_dithermode,
input uint9 crop_left,
input uint9 crop_right,
input uint8 crop_top,
input uint8 crop_bottom,
input int11 gpu_x,
input int11 gpu_y,
input uint7 gpu_colour,
input uint7 gpu_colour_alt,
input int11 gpu_param0,
input int11 gpu_param1,
input int11 gpu_param2,
input int11 gpu_param3,
input int11 gpu_param4,
input int11 gpu_param5,
input uint4 gpu_write,
input uint4 gpu_dithermode,
input uint7 pb_colour7,
input uint8 pb_colour8r,
input uint8 pb_colour8g,
input uint8 pb_colour8b,
input uint2 pb_newpixel,
output uint1 gpu_active(0),
) <autorun,reginputs> {
// GPU SUBUNITS
rectangle GPUrectangle(
crop_left <: crop_left, crop_right <: crop_right, crop_top <: crop_top, crop_bottom <: crop_bottom,
x <: gpu_x, y <: gpu_y, x1 <: gpu_param0, y1 <: gpu_param1
);
line GPUline(
x <: gpu_x, y <: gpu_y, x1 <: gpu_param0, y1 <: gpu_param1,
width <: gpu_param2[0,8]
);
circle GPUcircle(
x <: gpu_x, y <: gpu_y, radius <: gpu_param0, sectormask <: gpu_param1
);
triangle GPUtriangle(
crop_left <: crop_left, crop_right <: crop_right, crop_top <: crop_top, crop_bottom <: crop_bottom,
x <: gpu_x, y <: gpu_y, x1 <: gpu_param0, y1 <: gpu_param1, x2 <: gpu_param2, y2 <: gpu_param3
);
blit GPUblit(
blit1tilemap <:> blit1tilemap,
characterGenerator8x8 <:> characterGenerator8x8,
colourblittilemap <:> colourblittilemap,
x <: gpu_x, y <: gpu_y,
tile <: gpu_param0[0,9], scale <: gpu_param1[0,2], action <: gpu_param2[0,3]
);
pixelblock GPUpixelblock(
x <: gpu_x, y <: gpu_y, width <: gpu_param0,
ignorecolour <: gpu_param1, colour7 <: pb_colour7,
colour8r <: pb_colour8r, colour8g <: pb_colour8g, colour8b <: pb_colour8b,
newpixel <: pb_newpixel
);
// GPU UNIT BUSY FLAGS
uint6 gpu_busy_flags <:: { GPUpixelblock.busy, |GPUblit.busy, GPUtriangle.busy, GPUcircle.busy, GPUrectangle.busy, GPUline.busy };
uint1 gpu_busy <:: ( |gpu_busy_flags );
// CONTROLS FOR BITMAP PIXEL WRITER AND GPU SUBUNITS
bitmap_write := GPUline.bitmap_write | GPUrectangle.bitmap_write | GPUcircle.bitmap_write |
GPUtriangle.bitmap_write | GPUblit.bitmap_write | GPUpixelblock.bitmap_write;
GPUrectangle.start := 0; GPUline.start := 0; GPUcircle.start := 0; GPUtriangle.start := 0; GPUblit.start := 0; GPUpixelblock.start := 0;
gpu_active := ( |gpu_write[1,3] ) | gpu_busy;
always_after {
switch( gpu_write ) {
case 0: {}
case 1: {
// SET PIXEL (X,Y) NO GPU ACTIVATION
gpu_active_dithermode = 0; bitmap_colour_write = gpu_colour; ( bitmap_x_write, bitmap_y_write ) = copycoordinates( gpu_x, gpu_y ); bitmap_write = 1;
}
default: {
// START THE GPU DRAWING UNIT - RESET DITHERMODE TO 0 (most common)
gpu_active_dithermode = 0; bitmap_colour_write = gpu_colour; bitmap_colour_write_alt = gpu_colour_alt;
switch( gpu_write ) {
default: {}
case 2: { GPUline.start = 1; } // DRAW LINE FROM (X,Y) to (PARAM0,PARAM1)
case 3: { gpu_active_dithermode = gpu_dithermode; GPUrectangle.start = 1; } // DRAW RECTANGLE FROM (X,Y) to (PARAM0,PARAM1)
case 4: { GPUcircle.filledcircle = 0; GPUcircle.start = 1; } // DRAW CIRCLE CENTRE (X,Y) with RADIUS PARAM0
case 5: { gpu_active_dithermode = gpu_dithermode; GPUcircle.filledcircle = 1; GPUcircle.start = 1; } // DRAW FILLED CIRCLE CENTRE (X,Y) with RADIUS PARAM0
case 6: { gpu_active_dithermode = gpu_dithermode; GPUtriangle.start = 1; } // DRAW FILLED TRIANGLE WITH VERTICES (X,Y) (PARAM0,PARAM1) (PARAM2,PARAM3)
case 7: { GPUblit.tilecharacter = 1; GPUblit.start = 1; } // BLIT 16 x 16 TILE PARAM0 TO (X,Y)
case 8: { GPUblit.tilecharacter = 0; GPUblit.start = 1; } // BLIT 8 x 8 CHARACTER PARAM0 TO (X,Y) as 8 x 8
case 9: { GPUblit.tilecharacter = 1; GPUblit.start = 2; } // BLIT 16 x 16 COLOUR TILE PARAM0 TO (X,Y) as 16 x 16
case 10: { GPUpixelblock.start = 1; } // START THE PIXELBLOCK WRITER AT (x,y) WITH WIDTH PARAM0, IGNORE COLOUR PARAM1
// 11
// 12
// 13
// 14
// 15 is quadrilateral, handled by the queue
}
}
}
if( gpu_busy ) {
// COPY OUTPUT TO THE BITMAP WRITER
onehot( gpu_busy_flags ) {
case 0: { ( bitmap_x_write, bitmap_y_write ) = copycoordinates( GPUline.bitmap_x_write, GPUline.bitmap_y_write ); }
case 1: { ( bitmap_x_write, bitmap_y_write ) = copycoordinates( GPUrectangle.bitmap_x_write, GPUrectangle.bitmap_y_write ); }
case 2: { ( bitmap_x_write, bitmap_y_write ) = copycoordinates( GPUcircle.bitmap_x_write, GPUcircle.bitmap_y_write ); }
case 3: { ( bitmap_x_write, bitmap_y_write ) = copycoordinates( GPUtriangle.bitmap_x_write, GPUtriangle.bitmap_y_write ); }
case 4: {
( bitmap_x_write, bitmap_y_write ) = copycoordinates( GPUblit.bitmap_x_write, GPUblit.bitmap_y_write );
if( GPUblit.busy[1,1] ) { bitmap_colour_write = GPUblit.bitmap_colour_write; }
}
case 5: { ( bitmap_x_write, bitmap_y_write ) = copycoordinates( GPUpixelblock.bitmap_x_write, GPUpixelblock.bitmap_y_write ); bitmap_colour_write = GPUpixelblock.bitmap_colour_write; }
}
}
}
}
// HELPER - DECIDE IF MIN/MAX ARE WITHIN CROP
algorithm istodraw(
input int11 crop_left,
input int11 crop_right,
input int11 crop_top,
input int11 crop_bottom,
input int11 min_x,
input int11 min_y,
input int11 max_x,
input int11 max_y,
output uint1 draw
) <autorun> {
always_after {
draw = ~|{ ( max_x < crop_left ), ( max_y < crop_top ), ( min_x > crop_right ), ( min_y > crop_bottom ) };
}
}
// RECTANGLE - OUTPUT PIXELS TO DRAW A RECTANGLE
algorithm preprectangle(
input uint1 start,
output uint1 busy(0),
input int11 crop_left,
input int11 crop_right,
input int11 crop_top,
input int11 crop_bottom,
input int11 x,
input int11 y,
input int11 param0,
input int11 param1,
output int11 min_x,
output int11 min_y,
output int11 max_x,
output int11 max_y,
output uint1 todraw
) <autorun> {
uint1 xcompareparam0 <:: ( x < param0 ); uint1 ycompareparam1 <:: ( y < param1 );
int11 x1 <:: xcompareparam0 ? x : param0; int11 y1 <:: ycompareparam1 ? y : param1;
int11 x2 <:: xcompareparam0 ? param0 : x; int11 y2 <:: ycompareparam1 ? param1 : y;
istodraw TODRAW( crop_left <: crop_left, crop_right <: crop_right, crop_top <: crop_top, crop_bottom <: crop_bottom,
min_x <: min_x, min_y <: min_y, max_x <: max_x, max_y <: max_y );
todraw := 0;
while(1) {
if( start ) {
busy = 1;
min_x = ( x1 < crop_left ) ? crop_left : x1;
min_y = ( y1 < crop_top ) ? crop_top : y1;
max_x = ( ( x2 > crop_right ) ? crop_right : x2 );
max_y = 1 + ( ( y2 > crop_bottom ) ? crop_bottom : y2 );
++:
todraw = TODRAW.draw;
busy = 0;
}
}
}
algorithm drawrectangle(
input uint1 start,
output uint1 busy(0),
input uint9 min_x,
input uint8 min_y,
input uint9 max_x,
input uint8 max_y,
output int11 bitmap_x_write,
output int11 bitmap_y_write,
output uint1 bitmap_write
) <autorun> {
bitmap_write := 0;
while(1) {
if( start ) {
busy = 1;
bitmap_x_write = min_x; bitmap_y_write = min_y; bitmap_write = 1;
while( busy ) {
if( bitmap_x_write != max_x ) {
bitmap_x_write = bitmap_x_write + 1;
} else {
bitmap_x_write = min_x; bitmap_y_write = bitmap_y_write + 1;
}
busy = ( bitmap_y_write != max_y );
bitmap_write = busy;
}
}
}
}
algorithm rectangle (
input uint1 start,
output uint1 busy(0),
input uint9 crop_left,
input uint9 crop_right,
input uint8 crop_top,
input uint8 crop_bottom,
input int11 x,
input int11 y,
input int11 x1,
input int11 y1,
output int11 bitmap_x_write,
output int11 bitmap_y_write,
output uint1 bitmap_write
) <autorun,reginputs> {
preprectangle PREP(
start <: start,
crop_left <: crop_left, crop_right <: crop_right, crop_top <: crop_top, crop_bottom <: crop_bottom,
x <: x, y <: y, param0 <: x1, param1 <: y1
);
drawrectangle RECTANGLE(
min_x <: PREP.min_x, min_y <: PREP.min_y, max_x <: PREP.max_x, max_y <: PREP.max_y,
bitmap_x_write :> bitmap_x_write, bitmap_y_write :> bitmap_y_write, bitmap_write :> bitmap_write,
start <: PREP.todraw
);
busy := start | PREP.busy | PREP.todraw | RECTANGLE.busy;
}
// LINE - OUTPUT PIXELS TO DRAW A LINE
algorithm prepline(
input uint1 start,
output uint1 todraw,
input int11 x,
input int11 y,
input int11 param0,
input int11 param1,
output int11 x1,
output int11 y1,
output int11 dx,
output int11 dy,
output uint1 dv,
output int11 numerator,
output int11 max_count
) <autorun> {
uint1 ylessparam1 <:: ( y < param1 );
todraw := 0;
always_after {
if( start ) {
// Setup drawing a line from x,y to param0,param1 of width param2 in colour
// Ensure LEFT to RIGHT AND if moving UP or DOWN
if( x < param0 ) { x1 = x; y1 = y; dv = ylessparam1; } else { x1 = param0; y1 = param1; dv = ~ylessparam1; }
// Absolute DELTAs
( dx ) = absdelta( x, param0 ); ( dy ) = absdelta( y, param1 );
// Numerator
if( dx > dy ) { numerator = ( dx >> 1 ); max_count = dx + 1; } else { numerator = -( dy >> 1 ); max_count = dy + 1; }
todraw = 1;
}
}
}
algorithm drawline(
input uint1 start,
output uint1 busy(0),
input int11 start_x,
input int11 start_y,
input int11 start_numerator,
input int11 dx,
input int11 dy,
input uint1 dv,
input int11 max_count,
input uint8 width,
output int11 bitmap_x_write,
output int11 bitmap_y_write,
output uint1 bitmap_write
) <autorun> {
int11 x = uninitialized; int11 xNEXT <:: x + n2dx;
int11 y = uninitialized; int11 yNEXT <:: n2dy ? (y + ( dv ? 1 : -1 )) : y;
int11 numerator = uninitialized;
int11 numerator2 <:: numerator; int11 newnumerator <:: numerator - ( n2dx ? dy : 0 ) + ( n2dy ? dx : 0 );
uint1 n2dx <:: numerator2 > (-dx); uint1 n2dy <:: numerator2 < dy;
uint1 dxdy <:: dx > dy;
int11 count = uninitialized; int11 countNEXT <:: count + 1;
int11 offset_x = uninitialised; int11 offset_xNEXT <:: offset_y + dxdy;
int11 offset_y = uninitialised; int11 offset_yNEXT <:: offset_x + ~dxdy;
int11 offset_start <:: -( width >> 1 );
uint8 pixel_count = uninitialised; uint8 pixel_countNEXT <:: pixel_count + 1;
bitmap_x_write := x + offset_x; bitmap_y_write := y + offset_y; bitmap_write := 0;
while(1) {
if( start ) {
busy = 1; x = start_x; y = start_y; numerator = start_numerator; count = 0;
if( width == 1 ) {
offset_x = 0; offset_y = 0;
while( count != max_count ) {
bitmap_write = 1;
numerator = newnumerator; x = xNEXT; y = yNEXT; count = countNEXT;
}
} else {
while( count != max_count ) {
pixel_count = 0; offset_x = dxdy ? 0 : offset_start; offset_y = dxdy ? offset_start : 0;
while( pixel_count != width ) {
bitmap_write = 1; offset_y = offset_xNEXT; offset_x = offset_yNEXT; pixel_count = pixel_countNEXT;
}
numerator = newnumerator; x = xNEXT; y = yNEXT; count = countNEXT;
}
}
busy = 0;
}
}
}
algorithm line(
input uint1 start,
output uint1 busy(0),
input int11 x,
input int11 y,
input int11 x1,
input int11 y1,
input uint8 width,
output int11 bitmap_x_write,
output int11 bitmap_y_write,
output uint1 bitmap_write
) <autorun,reginputs> {
prepline PREP(
start <: start,
x <: x, y <: y, param0 <: x1, param1 <: y1
);
drawline LINE(
start_x <: PREP.x1, start_y <: PREP.y1,
start_numerator <: PREP.numerator,
dx <: PREP.dx, dy <: PREP.dy, dv <: PREP.dv,
max_count <: PREP.max_count, width <: width,
bitmap_x_write :> bitmap_x_write, bitmap_y_write :> bitmap_y_write,
bitmap_write :> bitmap_write,
start <: PREP.todraw
);
busy := start | PREP.todraw | LINE.busy;
}
// CIRCLE - OUTPUT PIXELS TO DRAW AN OUTLINE OR FILLED CIRCLE
algorithm drawcircle(
input uint1 start,
output uint1 busy(0),
input int11 xc,
input int11 yc,
input int11 radius,
input int11 start_numerator,
input uint8 draw_sectors,
input uint1 filledcircle,
output int11 bitmap_x_write,
output int11 bitmap_y_write,
output uint1 bitmap_write
) <autorun> {
int11 numerator = uninitialised; int11 new_numerator <:: numerator[10,1] ? numerator + { active_x, 2b00 } + 6 : numerator + { (active_x - active_y), 2b00 } + 10;
uint1 positivenumerator <:: ~numerator[10,1] & ( |numerator );
int11 active_x = uninitialized; int11 active_xNEXT <:: active_x + 1;
int11 active_y = uninitialized; int11 active_yNEXT <:: active_y - positivenumerator;
int11 count = uninitialised; int11 countNEXT <:: filledcircle ? count - 1 : min_count;
int11 min_count = uninitialised; int11 min_countNEXT <:: min_count + 1;
uint1 drawingcircle <:: ( active_y >= active_x ); uint1 finishsegment <:: ( countNEXT == min_count );
// PLUS OR MINUS OFFSETS
int11 xcpax <:: xc + active_x; int11 xcnax <:: xc - active_x;
int11 xcpc <:: xc + count; int11 xcnc <:: xc - count;
int11 ycpax <:: yc + active_x; int11 ycnax <:: yc - active_x;
int11 ycpc <:: yc + count; int11 ycnc <:: yc - count;
bitmap_write := 0;
while(1) {
if( start ) {
busy = 1;
active_x = 0; active_y = radius; count = radius; min_count = (-1); numerator = start_numerator;
while( drawingcircle ) {
if( ~|count & ~|active_x & |draw_sectors ) {
// DETECT IF CENTRE PIXEL, OUTPUT ONCE
bitmap_x_write = xc; bitmap_y_write = yc; bitmap_write = 1;
} else {
// OUTPUT PIXELS IN THE 8 SEGMENTS/ARCS AS PER MASK
bitmap_x_write = xcpax; bitmap_y_write = ycpc; bitmap_write = draw_sectors[0,1]; ++:
bitmap_y_write = ycnc; bitmap_write = draw_sectors[1,1]; ++:
bitmap_x_write = xcnax; bitmap_write = draw_sectors[2,1]; ++:
bitmap_y_write = ycpc; bitmap_write = draw_sectors[3,1]; ++:
bitmap_x_write = xcpc; bitmap_y_write = ycpax; bitmap_write = draw_sectors[4,1]; ++:
bitmap_y_write = ycnax; bitmap_write = draw_sectors[5,1]; ++:
bitmap_x_write = xcnc; bitmap_write = draw_sectors[6,1]; ++:
bitmap_y_write = ycpax; bitmap_write = draw_sectors[7,1];
}
if( finishsegment ) {
active_x = active_xNEXT; active_y = active_yNEXT; count = active_y; min_count = min_countNEXT; numerator = new_numerator;
} else {
count = countNEXT;
}
}
busy = 0;
}
}
}
algorithm circle(
input uint1 start,
output uint1 busy(0),
input int11 x,
input int11 y,
input int11 radius,
input uint8 sectormask,
input uint1 filledcircle,
output int11 bitmap_x_write,
output int11 bitmap_y_write,
output uint1 bitmap_write
) <autorun,reginputs> {
int11 absradius <:: radius[10,1] ? -radius : radius;
int11 gpu_numerator <:: 3 - ( { absradius, 1b0 } );
uint8 draw_sectors <:: { sectormask[5,1], sectormask[6,1], sectormask[1,1], sectormask[2,1], sectormask[4,1], sectormask[7,1], sectormask[0,1], sectormask[3,1] };
drawcircle CIRCLE(
xc <: x, yc <: y,
radius <: absradius,
start_numerator <: gpu_numerator,
draw_sectors <: draw_sectors,
filledcircle <: filledcircle,
bitmap_x_write :> bitmap_x_write, bitmap_y_write :> bitmap_y_write, bitmap_write :> bitmap_write,
start <: start
);
busy := start | CIRCLE.busy;
}
// TRIANGLE - OUTPUT PIXELS TO DRAW A FILLED TRIANGLE
algorithm swaponcondition(
input int11 x1,
input int11 x2,
input int11 y1,
input int11 y2,
input uint1 condition,
output int11 nx1,
output int11 nx2,
output int11 ny1,
output int11 ny2
) <autorun,reginputs> {
always_after {
if( condition ) {
nx1 = x2; ny1 = y2;
nx2 = x1; ny2 = y1;
} else {
nx1 = x1; ny1 = y1;
nx2 = x2; ny2 = y2;
}
}
}
algorithm min3(
input int11 n1,
input int11 n2,
input int11 n3,
output int11 min
) <autorun> {
always_after {
min = ( n1 < n2 ) ? ( ( n1 < n3 ) ? n1 : n3 ) : ( ( n2 < n3 ) ? n2 : n3 );
}
}
algorithm max3(
input int11 n1,
input int11 n2,
input int11 n3,
output int11 max
) <autorun> {
always_after {
max = ( n1 > n2 ) ? ( ( n1 > n3 ) ? n1 : n3 ) : ( ( n2 > n3 ) ? n2 : n3 );
}
}
algorithm preptriangle(
input uint1 start,
output uint1 busy(0),
input int11 crop_left,
input int11 crop_right,
input int11 crop_top,
input int11 crop_bottom,
input int11 x,
input int11 y,
input int11 param0,
input int11 param1,
input int11 param2,
input int11 param3,
output int11 x1,
output int11 y1,
output int11 x2,
output int11 y2,
output int11 x3,
output int11 y3,
output uint9 min_x,
output uint8 min_y,
output uint9 max_x,
output uint8 max_y,
output uint1 todraw
) <autorun> {
swaponcondition SWAP();
// Find minimum and maximum of x, x1, x2, y, y1 and y2 for the bounding box
min3 Xmin( n1 <: x1, n2 <: x2, n3 <: x3 ); min3 Ymin( n1 <: y1, n2 <: y2, n3 <: y3 );
max3 Xmax( n1 <: x1, n2 <: x2, n3 <: x3 ); max3 Ymax( n1 <: y1, n2 <: y2, n3 <: y3 );
istodraw TODRAW( crop_left <: crop_left, crop_right <: crop_right, crop_top <: crop_top, crop_bottom <: crop_bottom,
min_x <: min_x, min_y <: min_y, max_x <: max_x, max_y <: max_y );
todraw := 0;
while(1) {
if( start ) {
busy = 1;
// Setup drawing a filled triangle x,y param0, param1, param2, param3 ( Copy to x1, y1, x2, y2, x3, y3 )
( x1, y1 ) = copycoordinates ( x, y );
( x2, y2 ) = copycoordinates ( param0, param1 );
( x3, y3 ) = copycoordinates ( param2, param3 );
// Put points in order so that ( x1, y1 ) is at top, then ( x2, y2 ) and ( x3, y3 ) are clockwise from there
SWAP.x1 = x2; SWAP.y1 = y2; SWAP.x2 = x3; SWAP.y2 = y3; SWAP.condition = ( y3 < y2 ); ++: x2 = SWAP.nx1; y2 = SWAP.ny1; x3 = SWAP.nx2; y3 = SWAP.ny2;
SWAP.x1 = x1; SWAP.y1 = y1; SWAP.x2 = x2; SWAP.y2 = y2; SWAP.condition = ( y2 < y1 ); ++: x1 = SWAP.nx1; y1 = SWAP.ny1; x2 = SWAP.nx2; y2 = SWAP.ny2;
SWAP.x1 = x1; SWAP.y1 = y1; SWAP.x2 = x3; SWAP.y2 = y3; SWAP.condition = ( y3 < y1 ); ++: x1 = SWAP.nx1; y1 = SWAP.ny1; x3 = SWAP.nx2; y3 = SWAP.ny2;
SWAP.x1 = x2; SWAP.y1 = y2; SWAP.x2 = x3; SWAP.y2 = y3; SWAP.condition = ( y3 < y2 ); ++: x2 = SWAP.nx1; y2 = SWAP.ny1; x3 = SWAP.nx2; y3 = SWAP.ny2;
SWAP.x1 = x1; SWAP.y1 = y1; SWAP.x2 = x2; SWAP.y2 = y2; SWAP.condition = ( ( y2 == y1 ) & ( x2 < x1 ) ); ++: x1 = SWAP.nx1; y1 = SWAP.ny1; x2 = SWAP.nx2; y2 = SWAP.ny2;
SWAP.x1 = x2; SWAP.y1 = y2; SWAP.x2 = x3; SWAP.y2 = y3; SWAP.condition = ( ( y2 != y1 ) & ( y3 >= y2 ) & ( x2 < x3 ) ); ++: x2 = SWAP.nx1; y2 = SWAP.ny1; x3 = SWAP.nx2; y3 = SWAP.ny2;
// Apply cropping rectangle
min_x = ( Xmin.min < crop_left ) ? crop_left : Xmin.min;
min_y = ( Ymin.min < crop_top ) ? crop_top : Ymin.min;
max_x = ( Xmax.max > crop_right ) ? crop_right : Xmax.max;
max_y = 1 + ( ( Ymax.max > crop_bottom ) ? crop_bottom : Ymax.max );
++:
todraw = TODRAW.draw;
busy = 0;
}
}
}
algorithm intriangle(
input int11 x0,
input int11 y0,
input int11 x1,
input int11 y1,
input int11 x2,
input int11 y2,
input int11 px,
input int11 py,
output uint1 IN
) <autorun> {
int22 step1 <:: (( x2 - x1 ) * ( py - y1 ) - ( y2 - y1 ) * ( px - x1 ));
int22 step2 <:: (( x0 - x2 ) * ( py - y2 ) - ( y0 - y2 ) * ( px - x2 ));
int22 step3 <:: (( x1 - x0 ) * ( py - y0 ) - ( y1 - y0 ) * ( px - x0 ));
always_after {
IN = ~|{ step1[21,1], step2[21,1], step3[21,1] };
}
}
algorithm drawtriangle(
input uint1 start,
output uint1 busy(0),
input uint9 min_x,
input uint8 min_y,
input uint9 max_x,
input uint8 max_y,
input int11 x0,
input int11 y0,
input int11 x1,
input int11 y1,
input int11 x2,
input int11 y2,
output int11 bitmap_x_write,
output int11 bitmap_y_write,
output uint1 bitmap_write
) <autorun> {
// Filled triangle calculations
// Is the point px,py inside the triangle given by x0,x1 x1,y1 x2,y2?
intriangle IS( x0 <: x0, x1 <: x1, x2 <: x2, px <: px, y0 <: y0, y1 <: y1, y2 <: y2, py <: py );
uint1 beenInTriangle = uninitialized;
// CLOSER TO LEFT OR RIGHT OF THE BOUNDING BOX
uint1 leftright <:: ( px - min_x ) < ( max_x - px );
// WORK COORDINATES AND DIRECTION
uint9 px = uninitialized; uint9 pxNEXT <:: px + ( dx ? 1 : (-1) );
uint8 py = uninitialized; uint8 pyNEXT <:: py + 1;
uint1 dx = uninitialized;
// DETECT IF AT LEFT/RIGHT/BOTTOM OF THE BOUNDING BOX
uint1 stillinline <:: ( dx & ( px != max_x ) ) | ( ~dx & ( px != min_x ));
uint1 working <:: ( py != max_y );
bitmap_x_write := px; bitmap_y_write := py; bitmap_write := busy & IS.IN;
always {
if( start ) {
busy = 1; dx = 1; px = min_x; py = min_y;
} else {
if( working ) {
beenInTriangle = IS.IN | beenInTriangle;
if( beenInTriangle ^ IS.IN ) {
// Exited the triangle, move to the next line
beenInTriangle = 0; py = pyNEXT; px = leftright ? min_x : max_x; dx = leftright;
} else {
// MOVE TO THE NEXT PIXEL ON THE LINE LEFT/RIGHT OR DOWN AND SWITCH DIRECTION IF AT END
if( stillinline ) { px = pxNEXT; } else { dx = ~dx; beenInTriangle = 0; py = pyNEXT; }
}
} else {
busy = 0;
}
}
}
}
algorithm triangle(
input uint1 start,
output uint1 busy(0),
input uint9 crop_left,
input uint9 crop_right,
input uint8 crop_top,
input uint8 crop_bottom,
input int11 x,
input int11 y,
input int11 x1,
input int11 y1,
input int11 x2,
input int11 y2,
output int11 bitmap_x_write,
output int11 bitmap_y_write,
output uint1 bitmap_write
) <autorun,reginputs> {
// VERTEX COORDINATES AND BOUNDING BOX
preptriangle PREP(
start <: start,
crop_left <: crop_left, crop_right <: crop_right, crop_top <: crop_top, crop_bottom <: crop_bottom,
x <: x, y <: y, param0 <: x1, param1 <: y1, param2 <: x2, param3 <: y2
);
uint1 TRIANGLEbusy = uninitialised;
drawtriangle TRIANGLE(
min_x <: PREP.min_x, max_x <: PREP.max_x, min_y <: PREP.min_y, max_y <: PREP.max_y,
x0 <: PREP.x1, y0 <: PREP.y1, x1 <: PREP.x2, y1 <: PREP.y2, x2 <: PREP.x3, y2 <: PREP.y3,
bitmap_x_write :> bitmap_x_write, bitmap_y_write :> bitmap_y_write, bitmap_write :> bitmap_write,
start <: PREP.todraw
);
busy := start | PREP.busy | PREP.todraw | TRIANGLE.busy;
}
// BLIT - ( tilecharacter == 0 ) OUTPUT PIXELS TO BLIT AN 8 x 8 CHARACTER ( PARAM1 == 0 as 8 x 8, == 1 as 16 x 16, == 2 as 32 x 32, == 3 as 64 x 64 )
// COLOURBLIT - OUTPUT PIXELS TO BLIT A 16 x 16 TILE ( PARAM1 == 0 as 16 x 16, == 1 as 32 x 32, == 2 as 64 x 64, == 3 as 128 x 128 )
algorithm blitscale(
input uint7 offset,
input uint2 scale,
output uint7 base,
output int11 scaled
) <autorun> {
base := offset;
always_after {
scaled = offset << scale;
}
}
algorithm blittilexy(
input uint7 px,
input uint7 py,
input uint3 action,
output uint4 xinblittile,
output uint3 xinchartile,
output uint4 yinblittile,
output uint3 yinchartile
) <autorun> {
uint4 revx4 <:: ~px[0,4]; uint4 revy4 <:: ~py[0,4];
uint3 revx3 <:: ~px[0,3]; uint3 revy3 <:: ~py[0,3];
uint1 action00 <:: ( ~|action[0,2] ); uint1 action01 <:: ( action[0,2] == 2b01 ); uint1 action10 <:: ( action[0,2] == 2b10 );
// find y and x positions within the tile/character bitmap handling rotation or reflection
always_after {
xinblittile = ( action[2,1] ? action00 ? revx4 : action01 ? py[0,4] : action10 ? px[0,4] : revy4 : action[0,1] ? px[0,4] : revx4 );
yinblittile = action[2,1] ? action00 ? py[0,4] : action01 ? px[0,4] : action10 ? revy4 : revx4 : action[1,1] ? revy4 : py[0,4];
xinchartile = ( action[2,1] ? action00 ? revx3 : action01 ? py[0,3] : action10 ? px[0,3] : revy3 : action[0,1] ? px[0,3] : revx3 );
yinchartile = action[2,1] ? action00 ? py[0,3] : action01 ? px[0,3] : action10 ? revy3 : revx3 : action[1,1] ? revy3 : py[0,3];
}
}
algorithm cololurblittilexy(
input uint7 px,
input uint7 py,
input uint3 action,
output uint4 xintile,
output uint4 yintile
) <autorun> {
uint4 revx <:: ~px[0,4]; uint4 revy <:: ~py[0,4];
uint1 action00 <:: ( ~|action[0,2] ); uint1 action01 <:: ( action[0,2] == 2b01 ); uint1 action10 <:: ( action[0,2] == 2b10 );
// find y and x positions within the tile bitmap handling rotation or reflection
always_after {
xintile = action[2,1] ? action00 ? px[0,4] : action01 ? revy : action10 ? revx : py[0,4] : action[0,1] ? revx : px[0,4];
yintile = action[2,1] ? action00 ? py[0,4] : action01 ? px[0,4] : action10 ? revy : revx : action[1,1] ? revy : py[0,4];
}
}
algorithm blit(
input uint2 start,
output uint2 busy(0),
simple_dualport_bram_port0 blit1tilemap,
simple_dualport_bram_port0 characterGenerator8x8,
simple_dualport_bram_port0 colourblittilemap,
input int11 x,
input int11 y,
input uint9 tile,
input uint2 scale,
input uint3 action,
output int11 bitmap_x_write,
output int11 bitmap_y_write,
output uint1 bitmap_write,
output uint7 bitmap_colour_write,
input uint1 tilecharacter
) <autorun,reginputs> {
// START POSITION ON THE SCREEN, POSITION IN TILE/CHARACTER AND PIXEL COUNT FOR SCALING
uint4 x2 = uninitialised; int11 x2NEXT <:: x2 + 1;
uint4 y2 = uninitialised; int11 y2NEXT <:: y2 + 1;
blitscale PXS( scale <: scale ); int11 pxNEXT <:: PXS.base + 1;
blitscale PYS( scale <: scale ); int11 pyNEXT <:: PYS.base + 1;
// MAX PIXELS IN TILE AND NUMBER OF TIMES TO USE EACH PIXEL
uint5 max_pixels <:: tilecharacter ? 16 : 8; uint4 max_count <:: ( 1 << scale );
// FIND X AND Y WITHIN THE TILE/CHARACTER BITMAP
blittilexy BTXY( px <: PXS.base, py <: PYS.base, action <: action );
cololurblittilexy CBTXY( px <: PXS.base, py <: PYS.base, action <: action );
blit1tilemap.addr0 := { tile, BTXY.yinblittile }; characterGenerator8x8.addr0 := { tile, BTXY.yinchartile }; colourblittilemap.addr0 := { tile, CBTXY.yintile, CBTXY.xintile };
bitmap_x_write := x + PXS.scaled + x2; bitmap_y_write := y + PYS.scaled + y2; bitmap_colour_write := colourblittilemap.rdata0; bitmap_write := 0;
while(1) {
if( |start ) {
busy = start;
PYS.offset = 0;
while( PYS.offset != max_pixels ) {
PXS.offset = 0;
while( PXS.offset != max_pixels ) {
y2 = 0;
while( y2 != max_count ) {
x2 = 0;
while( x2 != max_count ) {
onehot( busy ) {
case 0: { bitmap_write = tilecharacter ? blit1tilemap.rdata0[BTXY.xinblittile, 1] : characterGenerator8x8.rdata0[BTXY.xinchartile, 1]; }
case 1: { bitmap_write = ~colour7( colourblittilemap.rdata0 ).alpha; }
}
x2 = x2NEXT;
}
y2 = y2NEXT;
}
PXS.offset = pxNEXT;
}
PYS.offset = pyNEXT;
}
busy = 0;
}
}
}
// PIXELBLOCK - OUTPUT PIXELS TO RECTANGLE START AT X, Y WITH WIDTH PARAM0, PIXELS PROVIDED SEQUENTIALLY BY CPU, MOVE ALONG RECTANGLE UNTIL STOP RECEIVED
// CAN HANDLE 7bit ( ARRGGBB ) colours, with one defined as transparent or 24bit RGB colours, scaling to the PAWS colour map
algorithm pixelblock(
input uint1 start,
output uint1 busy(0),
input int11 x,
input int11 y,
input int11 width,
input uint7 ignorecolour,
input uint7 colour7,
input uint8 colour8r,
input uint8 colour8g,
input uint8 colour8b,
input uint2 newpixel,
output int11 bitmap_x_write,
output int11 bitmap_y_write,
output uint7 bitmap_colour_write,
output uint1 bitmap_write
) <autorun,reginputs> {
uint1 update = uninitialised; uint1 lineend <:: ( bitmap_x_write == x + width - 1 );
bitmap_write := ( ( newpixel == 1 ) & ( colour7 != ignorecolour ) ) | ( newpixel == 2 );
bitmap_colour_write := ( newpixel == 1 ) ? colour7 : { 1b0, colour8r[6,2], colour8g[6,2], colour8b[6,2] };
always_after {
if( start ) {
busy = 1; bitmap_x_write = x; bitmap_y_write = y;
} else {
if( busy ) {
if( &newpixel ) {
busy = 0;
} else {
if( update ) {
if( lineend ) {
bitmap_x_write = x; bitmap_y_write = bitmap_y_write + 1;
} else {
bitmap_x_write = bitmap_x_write + 1;
}
update = 0;
} else {
update = |newpixel;
}
}
}
}
}
if( ~reset ) { busy = 0; }
}
// Vector Block
// Stores blocks of upto 16 vertices which can be sent to the GPU for line drawing
// Each vertices represents a delta from the centre of the vector
// Deltas are stored as 6 bit 2's complement range -31 to 0 to 31
// Each vertices has an active flag, processing of a vector block stops when the active flag is 0
// Each vector block has a centre x and y coordinate and a colour { rrggbb } when drawn
// SCALE A DELTA USING THE SCALE ATTRIBUTE
algorithm scaledetla(
input uint3 scale,
input uint6 delta,
output int11 scaled
) <autorun> {
// SIGN EXTEND THE DELTA, THEN SCALE
int11 extdelta <:: { {11{delta[5,1]}}, delta };
always_after {
scaled = ( scale[2,1] ? ( __signed(extdelta) >>> scale[0,2] ) : ( extdelta << scale[0,2] ) );
}
}
// ADJUST COORDINATES BY DELTAS AND SCALE
algorithm centreplusdelta(
input int11 xc,
input uint6 dx,
input int11 yc,
input uint6 dy,
input uint3 scale,
input uint3 action,
output int11 xdx,
output int11 ydy
) <autorun> {
// SIGN EXTEND DELTAS AND APPLY SCALE
scaledetla SDX( scale <: scale, delta <: dx ); scaledetla SDY( scale <: scale, delta <: dy );
// PLUS OR MINUS SCALE
int11 xcpdx <:: xc + SDX.scaled; int11 xcndx <:: xc - SDX.scaled;
int11 ycpdy <:: yc + SDY.scaled; int11 ycndy <:: yc - SDY.scaled;
int11 xcpdy <:: xc + SDY.scaled; int11 xcndy <:: xc - SDY.scaled;
int11 ycpdx <:: yc + SDX.scaled; int11 ycndx <:: yc - SDX.scaled;
always_after {
if( action[2,1] ) {
// ROTATION
switch( action[0,2] ) {
case 0: { xdx = xcpdx; ydy = ycpdy; }
case 1: { xdx = xcndy; ydy = ycpdx; }
case 2: { xdx = xcndx; ydy = ycndy; }