-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathDLX.sml
2840 lines (2530 loc) · 89.2 KB
/
DLX.sml
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
(* Added val _ = Main.doit() at end of file -- mael 2001-10-19 *)
(* Changed doit 500 to doit 100 -- mael 2001-10-19 *)
(* Minor tweaks by Stephen Weeks ([email protected]) on 2001-07-17 to turn into a
* benchmark.
* Added rand function.
*)
(*
* Matthew Thomas Fluet
* Harvey Mudd College
* Claremont, CA 91711
* e-mail: [email protected]
*
* A DLX Simulator in Standard ML
*
* Description:
* The DLX Simulator is a partial implementation of the RISC instruction
* set described in Patterson's and Hennessy's _Computer Architecture_.
* Currently, the DLX Simulator implements the following instructions:
* ADD ADDI
* ADDU ADDUI
* SUB SUBI
* SUBU SUBUI
* AND ANDI
* OR ORI
* XOR XORI
*
* LHI
*
* SLL SLLI
* SRL SRLI
* SRA SRAI
*
* SEQ SEQI
* SNE SNEI
* SLT SLTI
* SGT SGTI
* SLE SLEI
* SGE SGEI
*
* LB LBU SB
* LH LHU SH
* LW SW
*
* BEQZ BNEZ
* J JR
* JAL JALR
*
* TRAP
*
* NOP
*
* Currently, the DLX Simulator uses 32 bit words for addressing and
* the register file and a 65535 word memory. To augment the memory
* a cache can be installed in the simulator, with a number of different
* caching options that can be made. Caches can also cache other caches,
* so realistic dual level caches can be simulated. Input and output
* is limited to requesting and outputing signed integers.
*
* Usage:
* DLXSimulatorCX.run_file : string -> unit
* DLXSimulatorCX.run_prog : string list -> unit;
* The DLXSimualatorCX family of structures represent different caches
* used on the simulator. The following table describes the different
* caches used:
* C1: a small level 1 cache
* DLXSimulatorCX.run_file attempts to open and execute the instructions
* in a file. DLXSimulatorCX.run_prog runs a set of instructions as
* a list of strings. Four programs are included here.
* Simple : simply outputs the number 42.
* Twos: performs the twos complement on an inputed number.
* Abs: performs the absolute value on an imputed number.
* Fact: performs the factorial on an inputed number.
* GCD: performs the greatest common divisor on two imputed numbers.
* After running, the DLX Simulator outputs a set of statistics
* concerning memory reads and writes, and cache hits and misses.
*
* Future Work:
* With the implementation of the PACK_REAL structures
* as documented in the SML'97 Basis Library, the remainder
* of the DLX instruction set should be implemented.
* Currently, without an efficient and correct means of
* converting a 32 bit word into a 32 bit float, it is
* difficult to incorporate these instructions.
* In order to finish following the current development
* model, a FPALU structure should be implemented as the
* floating point arithmetic-logic unit.
* Another possibility for future work would be to
* model a pipelined processor. Currently, the DLX Simulator
* uses a simple one cycle per instruction model.
* It should be possible to break this model and implement
* a pipeline, but it would mean a major reworking of the
* DLXSimulatorFun functor.
*
* References:
* Patterson, David A. and John L. Hennessy. _Computer Architecture: A
* Quantitative Approach: Second Edition_. San Francisco: Morgan
* Kaufmann Publishers, Inc., 1996.
*
*)
(* ************************************************************************* *)
(* sweeks added rand *)
local
open Word
val seed: word ref = ref 0w13
in
(* From page 284 of Numerical Recipes in C. *)
fun rand (): word =
let
val res = 0w1664525 * !seed + 0w1013904223
val _ = seed := res
in
res
end
end
(*
* ImmArray.sml
*
* The ImmArray structure defines an immutable array implementation.
* An immarray is stored internally as a list.
* This results in O(n) sub and update functions, as opposed
* to O(1) sub and update functions found in Array. However,
* immutable arrays are truly immutable, and can be integrated
* with a functionally programming style easier than mutable
* arrays.
*
* The ImmArray structure mimics the Array structure as much as possible.
* The most obvious deviation is that unit return types in Array are replaced
* by 'a immarray return types in ImmArray. Unlike an 'a array, an 'a immarray
* is an equality type if and only if 'a is an equality type. Further immarray
* equality is structural, rather than the "creation" equality used by Array.
* Additionally, no vector type is supported, and consequently no copyVec
* function is supported. Finally, the functions mapi and map provide
* similar functionality as modifyi and modify, but relax the constraint that
* the argument function need be of type 'a -> 'a.
*
* Future Work : There are random-access list implementations
* that support O(log n) sub and update functions,
* which may provide a faster implementation, although
* possibly at the expense of space and the ease of
* implementing app, foldl, foldr, modify, and map functions.
*)
signature IMMARRAY
= sig
type 'a immarray;
val maxLen : int;
val immarray : (int * 'a) -> 'a immarray;
val fromList : 'a list -> 'a immarray;
val toList : 'a immarray -> 'a list;
val tabulate : int * (int -> 'a) -> 'a immarray;
val length : 'a immarray -> int;
val sub : 'a immarray * int -> 'a;
val update : 'a immarray * int * 'a -> 'a immarray;
val extract : 'a immarray * int * int option -> 'a immarray;
val copy : {src : 'a immarray, si : int, len : int option,
dst : 'a immarray, di : int} -> 'a immarray;
val appi : (int * 'a -> unit) -> ('a immarray * int * int option)
-> unit;
val app : ('a -> unit) -> 'a immarray -> unit;
val foldli : ((int * 'a * 'b) -> 'b) -> 'b
-> ('a immarray * int * int option) -> 'b;
val foldri : ((int * 'a * 'b) -> 'b) -> 'b
-> ('a immarray * int * int option) -> 'b;
val foldl : (('a * 'b) -> 'b) -> 'b -> 'a immarray -> 'b;
val foldr : (('a * 'b) -> 'b) -> 'b -> 'a immarray -> 'b;
val mapi : ((int * 'a) -> 'b) -> ('a immarray * int * int option)
-> 'b immarray;
val map : ('a -> 'b) -> 'a immarray -> 'b immarray;
val modifyi : ((int * 'a) -> 'a) -> ('a immarray * int * int option)
-> 'a immarray;
val modify : ('a -> 'a) -> 'a immarray -> 'a immarray;
end;
structure ImmArray : IMMARRAY
= struct
(* datatype 'a immarray
* An immarray is stored internally as a list.
* The use of a constructor prevents list functions from
* treating immarray type as a list.
*)
datatype 'a immarray = IA of 'a list;
(* val maxLen : int
* The maximum length of immarrays supported.
* Technically, under this implementation, the maximum length
* of immarrays is the same as the maximum length of a list,
* but for convience and compatibility, use the Array structure's
* maximum length.
*)
val maxLen = Array.maxLen;
(* val tabulate : int * (int -> 'a) -> 'a immarray
* val immarray : int * 'a -> 'a immarray
* val fromList : 'a list -> 'a immarray
* val toList : 'a immarray -> 'a list
* val length : 'a immarray -> int
* These functions perform basic immarray functions.
* The tabulate, immarray, and fromList functions create an immarray.
* The toList function converts an immarray to a list.
* The length function returns the length of an immarray.
*)
fun tabulate (n, initfn) = IA (List.tabulate (n, initfn));
fun immarray (n, init) = tabulate (n, fn _ => init);
fun fromList l = IA l;
fun toList (IA ia) = ia;
fun length (IA ia) = List.length ia;
(* val sub : 'a immarray * int -> 'a
* val update : 'a immarray * int * 'a -> 'a immarray
* These functions sub and update an immarray by index.
*)
fun sub (IA ia, i) = List.nth (ia, i);
fun update (IA ia, i, x) = IA ((List.take (ia, i)) @
(x::(List.drop (ia, i + 1))));
(* val extract : 'a immarray * int * int option -> 'a immarray
* This function extracts an immarray slice from an immarray from
* one index either through the rest of the immarray (NONE)
* or for n elements (SOME n), as described in the
* Standard ML Basis Library.
*)
fun extract (IA ia, i, NONE) = IA (List.drop (ia, i))
| extract (IA ia, i, SOME n) = IA (List.take (List.drop (ia, i), n));
(* val copy : {src : 'a immarray, si : int, len : int option,
dst : 'a immarray, di : int} -> 'a immarray
* This function copies an immarray slice from src into dst starting
* at the di element.
*)
fun copy {src, si, len, dst=IA ia, di}
= let
val IA sia = extract (src, si, len);
val pre = List.take (ia, di);
val post = case len
of NONE => List.drop (ia, di+(List.length sia))
| SOME n => List.drop (ia, di+n);
in
IA (pre @ sia @ post)
end;
(* val appi : ('a * int -> unit) -> ('a immarray * int * int option)
* -> unit
* val app : ('a -> unit) -> 'a immarray -> unit
* These functions apply a function to every element
* of an immarray. The appi function also provides the
* index of the element as an argument to the applied function
* and uses an immarray slice argument.
*)
local
fun appi_aux f i [] = ()
| appi_aux f i (h::t) = (f(i,h); appi_aux f (i + 1) t);
in
fun appi f (IA ia, i, len) = let
val IA sia = extract (IA ia, i, len);
in
appi_aux f i sia
end;
end;
fun app f immarr = appi (f o #2) (immarr, 0, NONE);
(* val foldli : (int * 'a * 'b -> 'b) -> 'b
* -> ('a immarray * int * int option) -> 'b;
* val foldri : (int * 'a * 'b -> 'b) -> 'b
* -> ('a immarray * int * int option) -> 'b;
* val foldl : ('a * 'b -> 'b) -> 'b -> 'a immarray -> 'b
* val foldr : ('a * 'b -> 'b) -> 'b -> 'a immarray -> 'b
* These functions fold a function over every element
* of an immarray. The foldri and foldli functions also provide
* the index of the element as an argument to the folded function
* and uses an immarray slice argument.
*)
local
fun foldli_aux f b i [] = b
| foldli_aux f b i (h::t) = foldli_aux f (f(i,h,b)) (i+1) t;
fun foldri_aux f b i [] = b
| foldri_aux f b i (h::t) = f(i,h,foldri_aux f b (i+1) t);
in
fun foldli f b (IA ia, i, len)
= let
val IA ia2 = extract (IA ia, i, len);
in
foldli_aux f b i ia2
end;
fun foldri f b (IA ia, i, len)
= let
val IA ia2 = extract (IA ia, i, len);
in
foldri_aux f b i ia2
end;
end;
fun foldl f b (IA ia) = foldli (fn (_,i,x) => f(i,x)) b (IA ia, 0, NONE);
fun foldr f b (IA ia) = foldri (fn (_,i,x) => f(i,x)) b (IA ia, 0, NONE);
(* val mapi : ('a * int -> 'b) -> 'a immarray -> 'b immarray
* val map : ('a -> 'b) -> 'a immarray -> 'b immarray
* These functions map a function over every element
* of an immarray. The mapi function also provides the
* index of the element as an argument to the mapped function
* and uses an immarray slice argument. Although there are
* similarities between mapi and modifyi, note that when mapi is
* used with an immarray slice, the resulting immarray is the
* same size as the slice. This is necessary to preserve the
* type of the resulting immarray. Thus, mapi with the identity
* function reduces to the extract function.
*)
local
fun mapi_aux f i [] = []
| mapi_aux f i (h::t) = (f (i,h))::(mapi_aux f (i + 1) t);
in
fun mapi f (IA ia, i, len) = let
val IA ia2 = extract (IA ia, i, len);
in
IA (mapi_aux f i ia2)
end;
end;
fun map f (IA ia)= mapi (f o #2) (IA ia, 0, NONE);
(* val modifyi : (int * 'a -> 'a) -> ('a immarray * int * int option)
* -> 'a immarray
* val modify : ('a -> 'a) -> 'a immarray -> 'a immarray
* These functions apply a function to every element of an immarray
* in left to right order and returns a new immarray where corresponding
* elements are replaced by their modified values. The modifyi
* function also provides the index of the element as an argument
* to the mapped function and uses an immarray slice argument.
*)
local
fun modifyi_aux f i [] = []
| modifyi_aux f i (h::t) = (f (i,h))::(modifyi_aux f (i + 1) t);
in
fun modifyi f (IA ia, i, len)
= let
val pre = List.take (ia, i);
val IA ia2 = extract (IA ia, i, len);
val post = case len
of NONE => []
| SOME n => List.drop (ia, i+n);
in
IA (pre @ (modifyi_aux f i ia2) @ post)
end;
end;
fun modify f (IA ia) = modifyi (f o #2) (IA ia, 0, NONE);
end;
(* ************************************************************************* *)
(*
* ImmArray2.sml
*
* The ImmArray2 structure defines a two dimensional immutable array
* implementation. An immarray2 is stored internally as an immutable
* array of immutable arrays. As such, the ImmArray2 makes heavy use
* of the ImmArray structure.
*
* The ImmArray2 structure mimics the Array2 structure as much as possible.
* The most obvious deviation is that unit return types in Array2 are replaced
* by 'a immarray2 return types in ImmArray2. Unlike an 'a array,
* an 'a immarray2 is an equality type if and only if 'a is an equality type.
* Further immarray2 equality is structural, rather than the "creation"
* equality used by Array2. Also, the 'a region type is not included in
* ImmArray2, but all functions in Array2 that require 'a regions are present
* with arguments taken in the natural order. Finally, the functions mapi
* and map provide similar functionality as modifyi and modify, but relax
* the constraint that the argument function need be of type 'a -> 'a.
*)
signature IMMARRAY2
= sig
type 'a immarray2;
datatype traversal = RowMajor | ColMajor
val immarray2 : int * int * 'a -> 'a immarray2;
val tabulate : traversal -> int * int * ((int * int) -> 'a)
-> 'a immarray2;
val fromList : 'a list list -> 'a immarray2;
val dimensions : 'a immarray2 -> int * int;
val sub : 'a immarray2 * int * int -> 'a;
val update : 'a immarray2 * int * int * 'a -> 'a immarray2;
val extract : 'a immarray2 * int * int * int option * int option
-> 'a immarray2;
val copy : {src : 'a immarray2, si : int, sj : int,
ilen : int option, jlen : int option,
dst : 'a immarray2, di : int, dj : int} -> 'a immarray2;
val nRows : 'a immarray2 -> int;
val nCols : 'a immarray2 -> int;
val row : 'a immarray2 * int -> 'a ImmArray.immarray;
val column : 'a immarray2 * int -> 'a ImmArray.immarray;
val appi : traversal -> (int * int * 'a -> unit)
-> ('a immarray2 * int * int * int option * int option)
-> unit;
val app : traversal -> ('a -> unit) -> 'a immarray2 -> unit;
val foldli : traversal -> ((int * int * 'a * 'b) -> 'b) -> 'b
-> ('a immarray2 * int * int * int option * int option)
-> 'b
val foldri : traversal -> ((int * int * 'a * 'b) -> 'b) -> 'b
-> ('a immarray2 * int * int * int option * int option)
-> 'b
val foldl : traversal -> (('a * 'b) -> 'b) -> 'b -> 'a immarray2 -> 'b
val foldr : traversal -> (('a * 'b) -> 'b) -> 'b -> 'a immarray2 -> 'b
val mapi : traversal -> (int * int * 'a -> 'b)
-> ('a immarray2 * int * int * int option * int option)
-> 'b immarray2;
val map : traversal -> ('a -> 'b) -> 'a immarray2 -> 'b immarray2;
val modifyi : traversal -> ((int * int * 'a) -> 'a)
-> ('a immarray2 * int * int * int option * int option)
-> 'a immarray2;
val modify : traversal -> ('a -> 'a) -> 'a immarray2 -> 'a immarray2;
end;
structure ImmArray2 : IMMARRAY2
= struct
(* datatype 'a immarray2
* An immarray2 is stored internally as an immutable array
* of immutable arrays. The use of a contructor prevents ImmArray
* functions from treating the immarray2 type as an immarray.
*)
datatype 'a immarray2 = IA2 of 'a ImmArray.immarray ImmArray.immarray;
datatype traversal = RowMajor | ColMajor
(* val tabulate : traversal -> int * int * (int * int -> 'a)
* -> 'a immarray2
* val immarray2 : int * int * 'a -> 'a immarray2
* val fromList : 'a list list -> 'a immarray2
* val dmensions : 'a immarray2 -> int * int
* These functions perform basic immarray2 functions.
* The tabulate and immarray2 functions create an immarray2.
* The fromList function converts a list of lists into an immarray2.
* Unlike Array2.fromList, fromList will accept lists of different
* lengths, allowing one to create an immarray2 in which the
* rows have different numbers of columns, although it is likely that
* exceptions will be raised when other ImmArray2 functions are applied
* to such an immarray2. Note that dimensions will return the
* number of columns in row 0.
* The dimensions function returns the dimensions of an immarray2.
*)
fun tabulate RowMajor (r, c, initfn)
= let
fun initrow r = ImmArray.tabulate (c, fn ic => initfn (r,ic));
in
IA2 (ImmArray.tabulate (r, fn ir => initrow ir))
end
| tabulate ColMajor (r, c, initfn)
= turn (tabulate RowMajor (c,r, fn (c,r) => initfn(r,c)))
and immarray2 (r, c, init) = tabulate RowMajor (r, c, fn (_, _) => init)
and fromList l
= IA2 (ImmArray.tabulate (length l,
fn ir => ImmArray.fromList (List.nth(l,ir))))
and dimensions (IA2 ia2) = (ImmArray.length ia2,
ImmArray.length (ImmArray.sub (ia2, 0)))
(* turn : 'a immarray2 -> 'a immarray2
* This function reverses the rows and columns of an immarray2
* to allow handling of ColMajor traversals.
*)
and turn ia2 = let
val (r,c) = dimensions ia2;
in
tabulate RowMajor (c,r,fn (cc,rr) => sub (ia2,rr,cc))
end
(* val sub : 'a immarray2 * int * int -> 'a
* val update : 'a immarray2 * int * int * 'a -> 'a immarray2
* These functions sub and update an immarray2 by indices.
*)
and sub (IA2 ia2, r, c) = ImmArray.sub(ImmArray.sub (ia2, r), c);
fun update (IA2 ia2, r, c, x)
= IA2 (ImmArray.update (ia2, r,
ImmArray.update (ImmArray.sub (ia2, r),
c, x)));
(* val extract : 'a immarray2 * int * int *
* int option * int option -> 'a immarray2
* This function extracts a subarray from an immarray2 from
* one pair of indices either through the rest of the
* immarray2 (NONE, NONE) or for the specfied number of elements.
*)
fun extract (IA2 ia2, i, j, rlen, clen)
= IA2 (ImmArray.map (fn ia => ImmArray.extract (ia, j, clen))
(ImmArray.extract (ia2, i, rlen)));
(* val nRows : 'a immarray2 -> int
* val nCols : 'a immarray2 -> int
* These functions return specific dimensions of an immarray2.
*)
fun nRows (IA2 ia2) = (#1 o dimensions) (IA2 ia2);
fun nCols (IA2 ia2) = (#2 o dimensions) (IA2 ia2);
(* val row : immarray2 * int -> ImmArray.immarray
* val column : immarray2 * int -> ImmArray.immarray
* These functions extract an entire row or column from
* an immarray2 by index, returning the row or column as
* an ImmArray.immarray.
*)
fun row (ia2, r) = let
val (c, _) = dimensions ia2;
in
ImmArray.tabulate (c, fn i => sub (ia2, r, i))
end;
fun column (ia2, c) = let
val (_, r) = dimensions ia2;
in
ImmArray.tabulate (r, fn i => sub (ia2, i, c))
end;
(* val copy : {src : 'a immarray2, si : int, sj : int,
* ilen : int option, jlen : int option,
* dst : 'a immarray2, di : int, dj : int};
* This function copies an immarray2 slice from src int dst starting
* at the di,dj element.
*)
fun copy {src, si, sj, ilen, jlen, dst=IA2 ia2, di, dj}
= let
val nilen = case ilen
of NONE => SOME ((nRows src) - si)
| SOME n => SOME n;
in
IA2 (ImmArray.modifyi (fn (r, ia)
=> ImmArray.copy {src=row (src, si+r-di),
si=sj, len=jlen,
dst=ia, di=dj})
(ia2, di, nilen))
end;
(* val appi : traversal -> ('a * int * int -> unit) -> 'a immarray2
* -> unit
* val app : traversal -> ('a -> unit) -> 'a immarray2 -> unit
* These functions apply a function to every element
* of an immarray2. The appi function also provides the
* indices of the element as an argument to the applied function
* and uses an immarray2 slice argument.
*)
fun appi RowMajor f (IA2 ia2, i, j, rlen, clen)
= ImmArray.appi (fn (r,ia) => ImmArray.appi (fn (c,x) => f(r,c,x))
(ia, j, clen))
(ia2, i, rlen)
| appi ColMajor f (ia2, i, j, rlen, clen)
= appi RowMajor (fn (c,r,x) => f(r,c,x)) (turn ia2, j, i, clen, rlen);
fun app tr f (IA2 ia2) = appi tr (f o #3) (IA2 ia2, 0, 0, NONE, NONE);
(* val foldli : traversal -> ((int * int * 'a * 'b) -> 'b) -> 'b
* -> ('a immarray2 * int * int * int option * int option)
* -> 'b
* val foldri : traversal -> ((int * int * 'a * 'b) -> 'b) -> 'b
* -> ('a immarray2 * int * int * int option * int option)
* -> 'b
* val foldl : traversal -> ('a * 'b -> 'b) -> 'b -> 'a immarray2 -> 'b
* val foldr : traversal -> ('a * 'b -> 'b) -> 'b -> 'a immarray2 -> 'b
* These functions fold a function over every element
* of an immarray2. The foldri and foldli functions also provide
* the index of the element as an argument to the folded function
* and uses an immarray2 slice argument.
*)
fun foldli RowMajor f b (IA2 ia2, i, j, rlen, clen)
= ImmArray.foldli (fn (r,ia,b)
=> ImmArray.foldli (fn (c,x,b) => f(r,c,x,b))
b
(ia, j, clen))
b
(ia2, i, rlen)
| foldli ColMajor f b (ia2, i, j, rlen, clen)
= foldli RowMajor (fn (c,r,x,b) => f(r,c,x,b)) b
(turn ia2, j, i, clen, rlen);
fun foldri RowMajor f b (IA2 ia2, i, j, rlen, clen)
= ImmArray.foldri (fn (r,ia,b)
=> ImmArray.foldri (fn (c,x,b) => f(r,c,x,b))
b
(ia, j, clen))
b
(ia2, i, rlen)
| foldri ColMajor f b (ia2, i, j, rlen, clen)
= foldri RowMajor (fn (c,r,x,b) => f(r,c,x,b)) b
(turn ia2, j, i, clen, rlen);
fun foldl tr f b (IA2 ia2)
= foldli tr (fn (_,_,x,b) => f(x,b)) b (IA2 ia2, 0, 0, NONE, NONE);
fun foldr tr f b (IA2 ia2)
= foldri tr (fn (_,_,x,b) => f(x,b)) b (IA2 ia2, 0, 0, NONE, NONE);
(* val mapi : traversal -> ('a * int * int -> 'b) -> 'a immarray2
* -> 'b immarray2
* val map : traversal -> ('a -> 'b) -> 'a immarray2 -> 'b immarray2
* These functions map a function over every element
* of an immarray2. The mapi function also provides the
* indices of the element as an argument to the mapped function
* and uses an immarray2 slice argument. Although there are
* similarities between mapi and modifyi, note that when mapi is
* used with an immarray2 slice, the resulting immarray2 is the
* same size as the slice. This is necessary to preserve the
* type of the resulting immarray2. Thus, mapi with the identity
* function reduces to the extract function.
*)
fun mapi RowMajor f (IA2 ia2, i, j, rlen, clen)
= IA2 (ImmArray.mapi (fn (r,ia) => ImmArray.mapi (fn (c,x) => f(r,c,x))
(ia, j, clen))
(ia2, i, rlen))
| mapi ColMajor f (ia2, i, j, rlen, clen)
= turn (mapi RowMajor (fn (c,r,x) => f(r,c,x))
(turn ia2, j, i, clen, rlen))
fun map tr f (IA2 ia2)
= mapi tr (f o #3) (IA2 ia2, 0, 0, NONE, NONE);
(* val modifyi : traversal -> (int * int* 'a -> 'a)
-> ('a immarray2 * int * int * int option * int option)
* -> 'a immarray2
* val modify : traversal -> ('a -> 'a) -> 'a immarray2 -> 'a immarray2
* These functions apply a function to every element of an immarray2
* in row by column order and returns a new immarray2 where corresponding
* elements are replaced by their modified values. The modifyi
* function also provides the index of the element as an argument
* to the mapped function and uses an immarray2 slice argument.
*)
fun modifyi RowMajor f (IA2 ia2, i, j, rlen, clen)
= IA2 (ImmArray.modifyi (fn (r,ia) => ImmArray.modifyi (fn (c,x)
=> f(r,c,x))
(ia, j, clen))
(ia2, i, rlen))
| modifyi ColMajor f (ia2, i, j, rlen, clen)
= turn (modifyi RowMajor (fn (c,r,x) => f(r,c,x))
(turn ia2, j, i, clen, rlen));
fun modify tr f (IA2 ia2)
= modifyi tr (f o #3) (IA2 ia2, 0, 0, NONE, NONE);
end;
(* ************************************************************************* *)
(*
* RegisterFile.sig
*
* This defines the exported datatype and functions provided by the
* register file. The datatype registerfile provides the encapsulation
* of the register file, InitRegisterFile initializes the registerfile,
* setting all registers to zero and setting r0, gp, sp, and fp to
* their appropriate values, LoadRegister takes a registerfile and
* an integer corresponding to the register, and returns the
* Word32.word value at that register, and StoreRegister takes a
* registerfile, an integer corresponding to the register, and a
* Word32.word and returns the registerfile updated with the word
* stored in the appropriate register.
*)
signature REGISTERFILE
= sig
type registerfile;
val InitRegisterFile : unit -> registerfile;
val LoadRegister : registerfile * int -> Word32.word;
val StoreRegister : registerfile * int * Word32.word -> registerfile;
end;
(*****************************************************************************)
(*
* RegisterFile.sml
*
* This defines the RegisterFile structure, which provides the
* functionality of the register file. The datatype registerfile
* provides the encapsulation of the register file, InitRegisterFile
* initializes the registerfile, setting all registers to zero and
* setting r0, gp, sp, and fp to their appropriate values,
* LoadRegister takes a registerfile and an integer corresponding to
* the register, and returns the Word32.word value at that register,
* and StoreRegister takes a registerfile, an integer corresponding to
* the register, and a Word32.word and returns the registerfile
* updated with the word stored in the appropriate register.
*
* The underlying structure of registerfile is an immutable array of
* Word32.word.
*)
structure RegisterFile : REGISTERFILE
= struct
type registerfile = Word32.word ImmArray.immarray;
fun InitRegisterFile ()
= ImmArray.update
(ImmArray.update
(ImmArray.update
(ImmArray.update
(ImmArray.immarray(32, 0wx00000000 : Word32.word),
00, 0wx00000000 : Word32.word),
28, 0wx00000000 : Word32.word),
29, 0wx00040000 : Word32.word),
30, 0wx00040000 : Word32.word) : registerfile;
fun LoadRegister (rf, reg) = ImmArray.sub(rf, reg);
fun StoreRegister (rf, reg, data) = ImmArray.update(rf, reg, data);
end;
(*****************************************************************************)
(*
* ALU.sig
*
* This defines the exported datatype and function provided by the
* ALU. The datatype ALUOp provides a means to specify which
* operation is to be performed by the ALU, and PerformAL performs
* one of the operations on two thirty-two bit words, returning the
* result as a thirty-two bit word.
*)
signature ALU
= sig
datatype ALUOp = SLL | SRL | SRA |
ADD | ADDU |
SUB | SUBU |
AND | OR | XOR |
SEQ | SNE |
SLT | SGT |
SLE | SGE;
val PerformAL : (ALUOp * Word32.word * Word32.word) -> Word32.word;
end;
(*****************************************************************************)
(*
* ALU.sml
*
* This defines the ALU structure, which provides the functionality of
* an Arithmetic/Logic Unit. The datatype ALUOp provides a means to
* specify which operation is to be performed by the ALU, and
* PerformAL performs one of the operations on two thirty-two bit
* words, returning the result as a thirty-two bit word.
*
* A note about SML'97 Basis Library implementation of thirty-two bit
* numbers: the Word32.word is an unsigned thirty-two bit integer,
* while Int.int (equivalent to Int.int) is a signed thirty-two
* bit integer. In order to perform the signed operations, it is
* necessary to convert the words to signed form, using the
* Word32.toIntX function, which performs sign extension,
* and to convert the result back into unsigned form using the
* Word32.fromInt function. In addition, to perform a shift,
* the second Word32.word needs to be "downsized" to a normal
* Word.word using the Word.fromWord function.
*)
structure ALU : ALU
= struct
datatype ALUOp = SLL | SRL | SRA |
ADD | ADDU |
SUB | SUBU |
AND | OR | XOR |
SEQ | SNE |
SLT | SGT |
SLE | SGE;
fun PerformAL (opcode, s1, s2) =
(case opcode
of SLL =>
Word32.<< (s1, Word.fromLargeWord (Word32.toLargeWord s2))
| SRL =>
Word32.>> (s1, Word.fromLargeWord (Word32.toLargeWord s2))
| SRA =>
Word32.~>> (s1, Word.fromLargeWord (Word32.toLargeWord s2))
| ADD =>
Word32.fromInt (Int.+ (Word32.toIntX s1,
Word32.toIntX s2))
| ADDU =>
Word32.+ (s1, s2)
| SUB =>
Word32.fromInt (Int.- (Word32.toIntX s1,
Word32.toIntX s2))
| SUBU =>
Word32.- (s1, s2)
| AND =>
Word32.andb (s1, s2)
| OR =>
Word32.orb (s1, s2)
| XOR =>
Word32.xorb (s1, s2)
| SEQ =>
if (s1 = s2)
then 0wx00000001 : Word32.word
else 0wx00000000 : Word32.word
| SNE =>
if not (s1 = s2)
then 0wx00000001 : Word32.word
else 0wx00000000 : Word32.word
| SLT =>
if Int.< (Word32.toIntX s1, Word32.toIntX s2)
then 0wx00000001 : Word32.word
else 0wx00000000 : Word32.word
| SGT =>
if Int.> (Word32.toIntX s1, Word32.toIntX s2)
then 0wx00000001 : Word32.word
else 0wx00000000 : Word32.word
| SLE =>
if Int.<= (Word32.toIntX s1, Word32.toIntX s2)
then 0wx00000001 : Word32.word
else 0wx00000000 : Word32.word
| SGE =>
if Int.>= (Word32.toIntX s1, Word32.toIntX s2)
then 0wx00000001 : Word32.word
else 0wx00000000 : Word32.word)
(*
* This handle will handle all ALU errors, most
* notably overflow and division by zero, and will
* print an error message and return 0.
*)
handle _ =>
(print "Error : ALU returning 0\n";
0wx00000000 : Word32.word);
end;
(*****************************************************************************)
(*
* Memory.sig
*
* This defines the exported datatype and functions provided by
* memory. The datatype memory provides the encapsulation
* of memory, InitMemory initializes memory, setting all
* addresses to zero, LoadWord takes memory and
* a Word32.word corresponding to the address, and returns the
* Word32.word value at that address, StoreWord takes memory,
* a Word32.word corresponding to the address, and a
* Word32.word and returns memory updated with the word
* stored at the appropriate address. LoadHWord, LoadHWordU,
* LoadByte, and LoadByteU load halfwords, unsigned halfwords,
* bytes, and unsigned bytes respectively from memory into the
* lower portion of the returned Word32.word. StoreHWord and
* StoreByte store halfwords and bytes taken from the lower portion
* of the Word32.word into memory.
* GetStatistics takes memory and returns the read and write
* statistics as a string.
*)
signature MEMORY
= sig
type memory;
val InitMemory : unit -> memory;
val LoadWord : memory * Word32.word -> memory * Word32.word;
val StoreWord : memory * Word32.word * Word32.word -> memory;
val LoadHWord : memory * Word32.word -> memory * Word32.word;
val LoadHWordU : memory * Word32.word -> memory * Word32.word;
val StoreHWord : memory * Word32.word * Word32.word -> memory;
val LoadByte : memory * Word32.word -> memory * Word32.word;
val LoadByteU : memory * Word32.word -> memory * Word32.word;
val StoreByte : memory * Word32.word * Word32.word -> memory;
val GetStatistics : memory -> string;
end;
(*****************************************************************************)
(*
* Memory.sml
*
* This defines the Memory structure, which provides the functionality
* of memory. The datatype memory provides the encapsulation of
* memory, InitMemory initializes memory, setting all
* addresses to zero, LoadWord takes memory and
* a Word32.word corresponding to the address, and returns the
* Word32.word value at that address and the updated memory,
* StoreWord takes memory, a Word32.word corresponding to the
* address, and a Word32.word and returns memory updated with the word
* stored at the appropriate address. LoadHWord, LoadHWordU,
* LoadByte, and LoadByteU load halfwords, unsigned halfwords,
* bytes, and unsigned bytes respectively from memory into the
* lower portion of the returned Word32.word. StoreHWord and
* StoreByte store halfwords and bytes taken from the lower portion
* of the Word32.word into memory.
* GetStatistics takes memory and returns the read and write
* statistics as a string.
*
* The underlying structure of memory is an immutable array of Word32.word.
* The array has a length of 0x10000, since every element of the array
* corresponds to a thirty-two bit integer.
*
* Also, the functions AlignWAddress and AlignHWAddress aligns a memory
* address to a word and halfword address, respectively. If LoadWord,
* StoreWord, LoadHWord, LoadHWordU, or StoreHWord is asked to access an
* unaligned address, it writes an error message, and uses the address
* rounded down to the aligned address.
*)
structure Memory : MEMORY
= struct
type memory = Word32.word ImmArray.immarray * (int * int);
fun InitMemory () =
(ImmArray.immarray(Word32.toInt(0wx10000 : Word32.word),
0wx00000000 : Word32.word),
(0, 0)) : memory;
fun AlignWAddress address
= Word32.<< (Word32.>> (address, 0wx0002), 0wx0002);
fun AlignHWAddress address
= Word32.<< (Word32.>> (address, 0wx0001), 0wx0001);
(* Load and Store provide errorless access to memory.
* They provide a common interface to memory, while
* the LoadX and StoreX specifically access words,
* halfwords and bytes, requiring address to be aligned.
* In Load and Store, two intermediate values are
* generated. The value aligned_address is the aligned
* version of the given address, and is used to compare with
* the original address to determine if it was aligned. The
* value use_address is equivalent to aligned_address divided
* by four, and it corresponds to the index of the memory
* array where the corresponding aligned address can be found.
*)
fun Load ((mem, (reads, writes)), address)
= let
val aligned_address = AlignWAddress address;
val use_address = Word32.>> (aligned_address, 0wx0002);
in
((mem, (reads + 1, writes)),
ImmArray.sub(mem, Word32.toInt(use_address)))
end;
fun Store ((mem, (reads, writes)), address, data)
= let
val aligned_address = AlignWAddress address;
val use_address = Word32.>> (aligned_address, 0wx0002);
in
(ImmArray.update(mem, Word32.toInt(use_address), data),
(reads, writes + 1))
end;
fun LoadWord (mem, address)
= let
val aligned_address
= if address = AlignWAddress address
then address
else (print "Error LW: Memory using aligned address\n";
AlignWAddress address);
in
Load(mem, aligned_address)
end;
fun StoreWord (mem, address, data)
= let
val aligned_address
= if address = AlignWAddress address
then address
else (print "Error SW: Memory using aligned address\n";
AlignWAddress address);
in
Store(mem, aligned_address, data)
end;
fun LoadHWord (mem, address)
= let
val aligned_address
= if address = AlignHWAddress address
then address
else (print "Error LH: Memory using aligned address\n";
AlignHWAddress address);
val (nmem,l_word) = Load(mem, aligned_address);
in
(nmem,
case aligned_address
of 0wx00000000 : Word32.word
=> Word32.~>>(Word32.<<(l_word, 0wx0010),
0wx0010)
| 0wx00000010 : Word32.word