ucisc 0.1.3 → 0.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,96 +1,87 @@
1
- # A recursive factorial algorithm
2
- # Inspired by the SubX example near the bottom of this post:
3
- # http://akkartik.name/post/mu-2019-1
4
- #
5
- # The goal is to compare the implementation of the uCISC ISA to x86
6
- # as well as roughly see how things line up with SubX and look for
7
- # the coming MuCISC goals
1
+ # Print hello world to the console
8
2
  #
9
3
  # To run:
10
- # $ exe/ucisc examples/factorial.ucisc
4
+ # $ exe/ucisc examples/hello_world.ucisc core/stdlib.ucisc
11
5
  #
12
- # Type 'break' when you hit the breakpoint, then `load(register(1))`
13
- # to view the result on the stack
6
+ # The stack should be empty when complete. It prints the following:
14
7
  #
15
- # More instructions on the compiler and debugger are in the README.md
16
- # Or use GitHub https://github.com/grokthis/ucisc-ruby#usage
8
+ # Hello, world! uCISC is here!
17
9
 
18
- # Setup some simple syntax sugar for easy reference to stack and pc
19
- $stack as 1.mem
10
+ ################
11
+ # Syntax Setup #
12
+ ################
20
13
  &pc as 0.reg
14
+ &banking as 4.reg
21
15
 
22
- # Simple conditional syntax sugar
16
+ # Conditionals
23
17
  $zero? as 0.eff
24
18
  $not_zero? as 1.eff
25
19
 
26
- Entry:
27
- # Init stack pointer
28
- copy 0.imm &stack
29
-
30
- # Functions calls reserve space on the stack (the stack size number in brackets)
31
- # Then they push the return address, followed by the args. The called function
32
- # is expected to remove the return address and args from the stack before returning
33
- $stack <= print_string(31.imm, hello.imm)
20
+ # Compute Ops
21
+ $shift_left as 0x5.op
22
+ $shift_right as 0x6.op
23
+ $high_byte as 0x8.op # zero least significant byte
24
+ $add as 0xA.op
34
25
 
35
- # halt
36
- copy &pc &pc
26
+ ################
27
+ # Code Entry #
28
+ ################
29
+ &stack as copy 0.imm 1.reg
37
30
 
38
- hello:
31
+ $stack[1] <= init_device(16.imm)
32
+ $&device_control = $stack
39
33
 
40
- # "\nHello, world! uCISC is here!\n\n" in hex
41
- % 0A48 656C 6C6f 2C20 776F 726C 6421 2075 4349 5343 2069 7320 6865 7265 210A 0A00
34
+ $stack <= print_string($&device_control, hello.imm)
42
35
 
43
- term_device:
44
- % 0100
36
+ # pop device control
37
+ copy &&device_control 1.imm &stack
45
38
 
46
- print_string: # (len, *string)
47
- # reference term control
48
- &term = copy &pc term_device.disp 2.reg
49
- copy $term &term
50
- # Save to stack for quick restore later
51
- copy &term $stack push
39
+ # halt
40
+ copy &pc &pc
52
41
 
53
- # Bank regions 0x0000 and 0x1000
54
- copy 3.imm 4.reg
55
- # reference term page
56
- compute 0x8.op/zero LSB/ $term 1.imm &term
42
+ ################
43
+ # Main Section #
44
+ ################
45
+ hello:
46
+ % "\nHello, world! uCISC is here!\n\n"
57
47
 
58
- # Bank region 0x1000
59
- copy 2.imm 4.reg
48
+ print_string: # (&device_control, &string)
49
+ $&string, $&device_control, $&return = $stack
60
50
 
61
- &str = copy $stack 1.imm 3.reg
62
- # Move length to top of stack
63
- copy $stack 2.imm $stack push
51
+ &device_control = copy $&device_control 3.reg
52
+ $&device_bank = $device_control 1.imm
64
53
 
65
- # Len is in bytes, add 1 and divide by 2 to get word len
66
- compute 0xA.op/add/ 1.imm $stack
67
- compute 0x6.op/shift >>/ 1.imm $stack
54
+ #bank control segment
55
+ copy 1.imm &banking
56
+ &stdout as compute $high_byte $&device_bank 3.reg
57
+ #bank stdout buffer segment
58
+ copy 2.imm &banking
68
59
 
69
- # Loop over string, copy to $term
70
- {
71
- copy &pc break.disp &pc $zero?
60
+ &string as copy $&string 2.reg
61
+ $string.len, $string.data = $string
72
62
 
73
- {
74
- copy $str $term
75
- compute 0xA.op/add/ 1.imm &str
76
- compute 0xA.op/add/ 1.imm &term
63
+ # Length is in bytes, add 1 and divide by 2 to get word len
64
+ $word_len = copy $string.len $stack push
65
+ compute $add 1.imm $word_len
66
+ compute $shift_right 1.imm $word_len
77
67
 
78
- compute 0xB.op/subtract/ 1.imm $stack
68
+ $stack <= mem_copy(&string.data, &stdout, $word_len)
79
69
 
80
- copy &pc loop.disp &pc $not_zero?
81
- }
82
- }
70
+ &string as copy $&string 2.reg
71
+ $string.len = $string
72
+ &device_control = copy $&device_control 3.reg
73
+ $stdout.read_count as $device_control 6.imm
74
+ # we need to put $string.len on the stack so banking doesn't make it invisible
75
+ $len = copy $string.len $stack push
83
76
 
84
- # restore terminal control pointer
85
- copy $stack 1.imm &term
86
- # Bank regions 0x0000 and 0x1000
87
- copy 3.imm 4.reg
77
+ #bank control segment
78
+ copy 1.imm &banking
88
79
  # write length to terminal control
89
- copy $stack 3.imm $term 5.imm
80
+ copy $len $stdout.read_count
90
81
  # Unbank all regions
91
- copy 0.imm 4.reg
92
-
93
- copy &stack 4.imm &stack
82
+ copy 0.imm &banking
94
83
 
95
84
  # Jump return
96
- copy $stack &pc pop
85
+ copy &&return &stack
86
+ copy $&return &pc pop
87
+
@@ -0,0 +1,261 @@
1
+ # Draws an image on the screen at 16, 16 (from the top left)
2
+ # Basic algorithm:
3
+ # *Setup*
4
+ # 1. The image data is part of the compiled output, but it
5
+ # must be paged in. Processors only get the first page
6
+ # of a task by default, so programs need to load themselves
7
+ # into local memory first.
8
+ # 2. Paint the image
9
+ #
10
+ # *Painting*
11
+ # For each line in the image:
12
+ # 1. Calculate page offset and count for a image raster line
13
+ # 2. Page in the relevant pages from main memory. Note, this
14
+ # isn't strictly necessary for this example as no other
15
+ # code is drawing to the screen, but it's a moral victory.
16
+ # 3. Copy the memory from the image data region to the proper
17
+ # location in the paged memory.
18
+ # 4. Page the memory back to main memory in the proper location.
19
+ # Since the video memory is mapped to the main memory at a
20
+ # specific location, this causes the line to draw immediately.
21
+ #
22
+ # The program halts when done and the CPU will idle.
23
+ #
24
+ # Notes:
25
+ # * Double buffering is not implemented and the necessary hardware
26
+ # is not emulated. So, the screen will "tear" if you don't draw
27
+ # fast enough.
28
+ # * In practice, the CPU can definitely outrun the paging mechanisms.
29
+ # I got some nice partial images when I turned off access blocking
30
+ # to pages that were in the process of paging.
31
+ # * This page in/page out mechanism kills at least half your available
32
+ # performance. It would be better to load multiple lines worth of
33
+ # pages at once, draw on them and page them out. That's a harder
34
+ # algorithm but in theory could preserve most of your performance.
35
+ # This is just a proof of concept so I didn't do that.
36
+ #
37
+ # Conventions:
38
+ #
39
+ # I put psuedo code in the comments for each instruction. Some notes
40
+ # about reading them and other things to know:
41
+ #
42
+ # * `var name = value` - var is created on the stack
43
+ #
44
+ # * Registers are generally treated as pointers
45
+ #
46
+ # * I tried to use C pointer semantics for comments. It's not perfect
47
+ # because some of the dereferencing is a bit different here and it's not
48
+ # checked by a compiler or running code so take them with a grain of salt.
49
+ #
50
+ # * The one exception is occaisionally they get loaded with an increment
51
+ # value. Sometimes you just need to add one real fast.
52
+ #
53
+ # * You can generally easily count stack offsets by counting 1.push args
54
+ #
55
+ # * Don't forget to add offsets for any vars returned by functions on
56
+ # the stack. I commented function return values for easy reference.
57
+ #
58
+ # * r1 is the stack, don't use it for anything else.
59
+ #
60
+ # * r2, r3 are not preserved by function calls, any one can do anything
61
+ # with them. Assume they are garbage after a function call.
62
+ #
63
+ # * One variable needed on every loop iteration is always on the top of
64
+ # the stack for easy access.
65
+ #
66
+ # * I found calculating before the loop to be pretty efficient. A quick
67
+ # subtract will set the zero flag and you can jump over the break easily.
68
+ #
69
+ # * The order is heavily influenced by the limits of function call
70
+ # immediate values. Notice the _page hack right off the bat.
71
+ #
72
+ # * I'll be updating function calls to handle this more gracefully.
73
+ #
74
+ # * `calculate_page_offset` uses 32-bit math. Check it out.
75
+ #
76
+ # To run:
77
+ # $ exe/ucisc examples/image.ucisc
78
+ #
79
+ # Then: view the image. It works!!
80
+ #
81
+ # Instructions on the compiler and debugger are in the README.md
82
+ # Or use GitHub https://github.com/grokthis/ucisc-ruby#usage
83
+
84
+ ################
85
+ # Syntax Setup #
86
+ ################
87
+ &stack as 1.reg
88
+ &pc as 0.reg
89
+ &banking as 4.reg
90
+
91
+ # Conditionals
92
+ $zero? as 0.eff
93
+ $not_zero? as 1.eff
94
+ $negative? as 2.eff
95
+
96
+ # Compute Ops
97
+ $invert as 0x0.op
98
+ $and as 0x1.op
99
+ $or as 0x2.op
100
+ $xor as 0x3.op
101
+ $increment as 0x4.op
102
+ $shift_left as 0x5.op
103
+ $shift_right as 0x6.op
104
+ $swap_bytes as 0x7.op
105
+ $high_byte as 0x8.op # zero least significant byte
106
+ $low_byte as 0x9.op # zero most significant byte
107
+ $add as 0xA.op
108
+ $subtract as 0xB.op
109
+ $multiply as 0xC.op
110
+ $divide as 0xD.op
111
+ $add_overflow as 0xE.op
112
+ $mask_to_overflow as 0xF.op
113
+
114
+ ################
115
+ # Code Entry #
116
+ ################
117
+ Entry:
118
+ $&return = $stack
119
+
120
+ $stack[1] <= stdlib_init_device(17.imm)
121
+ $&device_control = $stack
122
+
123
+ $y = copy 10.imm $stack push
124
+ $x = copy 0.imm $stack push
125
+ {
126
+ $stack <= paint_image(&pc image.disp, $x, $y, $&device_control)
127
+ $stack <= paint_image(&pc blank_left.disp, $x, $y, $&device_control)
128
+
129
+ compute $add 1.imm $x
130
+ compute $low_byte $x $x
131
+
132
+ copy &pc loop.disp &pc
133
+ }
134
+
135
+ # Image:struct
136
+ # pixels:*word[]
137
+ # width:int
138
+ # height:int
139
+ image:
140
+ % image.data.imm
141
+ % 0040 # width: 64
142
+ % 0030 # height: 48
143
+
144
+ blank_left:
145
+ % 2000
146
+ % 0001 # width: 1
147
+ % 0030 # height: 48
148
+
149
+ stdlib_init_device:
150
+ copy &pc init_device.disp &pc
151
+ stdlib_mem_copy:
152
+ copy &pc mem_copy.disp &pc
153
+
154
+ # $stack <= paint_image($&image, $x, $y, $&screen_device)
155
+ paint_image:
156
+ $&screen_device, $y, $x, $&image, $&return = $stack
157
+
158
+ $screen_device as 3.mem
159
+ copy $&screen_device &screen_device
160
+ $screen_device.w, $screen_device.h = $screen_device 6.imm
161
+
162
+ &image = copy $&image 2.reg
163
+ $image.data, $image.width, $image.height as $image
164
+
165
+ $c_row = copy 0.imm $stack push
166
+ $c_y = copy $y $stack push
167
+ $end_y = copy $y $stack push
168
+ compute $add $image.height $end_y
169
+
170
+ # Cap $end_y to screen height
171
+ copy 1.imm &banking
172
+ $max = copy $screen_device.h $stack push
173
+ $diff = compute $subtract $end_y $max
174
+ compute $multiply -1.imm $diff
175
+ { # if end_y > screen.height
176
+ copy &pc break.disp &pc $negative?
177
+
178
+ copy $screen_device.h $end_y # Cap at screen height
179
+ }
180
+ copy &end_y &stack
181
+ copy 0.imm &banking
182
+
183
+ {
184
+ $remaining = compute 0xB.op $c_y $end_y push
185
+ copy &pc break.disp &pc $negative?
186
+ copy &pc break.disp &pc $zero?
187
+
188
+ { # while c_y < end_y
189
+ $stack <= paint_line($x, $c_y, $c_row, &image, &screen_device)
190
+ # These can get destroyed by called methods
191
+ copy $&image &image
192
+ copy $&screen_device &screen_device
193
+
194
+ # Increment row and y vars
195
+ $tmp = copy $c_row $stack push
196
+ $tmp = compute $add 1.imm $tmp
197
+ copy $tmp $c_row
198
+ $tmp = copy $c_y $stack push
199
+ compute $add 1.imm $tmp
200
+ copy $tmp $c_y
201
+
202
+ copy &end_y &stack
203
+
204
+ $remaining = compute 0xB.op $c_y $end_y push
205
+ copy &pc loop.disp &pc $not_zero?
206
+ }
207
+ }
208
+
209
+ copy &&return &stack # pop all args
210
+ copy $&return &pc pop
211
+
212
+ stdlib_mem_copy2:
213
+ copy &pc stdlib_mem_copy.disp &pc
214
+
215
+ # $stack <= paint_line($x, $y, $image_row, $&image, $&screen_device)
216
+ paint_line:
217
+ $&screen_device, $&image, $image_row, $y, $x, $&return = $stack
218
+
219
+ &screen_device = copy $&screen_device 3.reg
220
+ $screen_device.bank = $screen_device 1.imm
221
+ $screen_device.block = $screen_device 3.imm
222
+ $screen_device.w, $screen_device.h = $screen_device 6.imm
223
+
224
+ &image as copy $&image 2.reg
225
+ $image.&data, $image.width, $image.height = $image
226
+
227
+ # 1. figure out which block we need
228
+ # screen row = y_offset + image_row
229
+ $screen_row = copy $y $stack push
230
+
231
+ $&source = copy $image_row $stack push
232
+ compute $multiply $image.width $&source
233
+ compute $add $image.&data $&source
234
+
235
+ copy 1.imm &banking
236
+ $row_offset = copy $screen_device.w $stack push
237
+ compute $multiply $screen_row $row_offset
238
+
239
+ $word_offset = copy $x $stack push
240
+ compute $add $row_offset $word_offset
241
+
242
+ $word_block_offset = compute $low_byte $word_offset $stack push
243
+ $&target = copy $screen_device.bank $stack push
244
+ compute $high_byte $&target $&target
245
+ compute $or $word_block_offset $&target
246
+
247
+ $block = copy $word_offset $stack push
248
+ compute $high_byte $block $block
249
+ compute $swap_bytes $block $block
250
+ copy $block $screen_device.block
251
+ copy 2.imm &banking
252
+
253
+ #$stack[1] <= banked_mem_copy($&source, $&target, $image.width, &screen_device)
254
+ $stack <= stdlib_mem_copy2($&source, $&target, $image.width)
255
+ #$copied = $stack
256
+
257
+ copy 0.imm &banking
258
+
259
+ copy &&return &stack
260
+ copy $&return &pc pop
261
+
@@ -0,0 +1,38 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "chunky_png"
5
+ require "byebug"
6
+
7
+ if ARGV.size < 2
8
+ puts "Usage:"
9
+ puts " png_to_hex <input_file> <output_file>"
10
+ exit(0)
11
+ end
12
+
13
+ image = ChunkyPNG::Image.from_file(ARGV.first)
14
+ rows = []
15
+ hex = []
16
+ image.pixels.each do |pixel|
17
+ rgba = "%08X" % pixel
18
+ pixel = pixel >> 8
19
+ b = (((pixel & 0xFF) + 8) & 0xF0) >> 4
20
+ pixel = pixel >> 8
21
+ g = (((pixel & 0xFF) + 8) & 0xF0) >> 4
22
+ pixel = pixel >> 8
23
+ r = (((pixel & 0xFF) + 8) & 0xF0) >> 4
24
+
25
+ color = (r << 8 | g << 4 | b )
26
+ hex << "%04X" % color
27
+
28
+ if hex.size == 16
29
+ rows << "% #{hex.join(" ")}"
30
+ hex = []
31
+ end
32
+ end
33
+
34
+ rows << "% #{hex.join(" ")}" if hex.size > 0
35
+ hex = []
36
+
37
+ File.open(ARGV.last, 'w') { |file| file.write(rows.join("\n")) }
38
+