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.
- checksums.yaml +4 -4
- data/Gemfile.lock +4 -1
- data/core/stdlib.ucisc +101 -0
- data/examples/everest_12bit.ucisc +195 -0
- data/examples/factorial.ucisc +24 -22
- data/examples/fib.ucisc +40 -35
- data/examples/hello_world.ucisc +59 -68
- data/examples/paint_image.ucisc +261 -0
- data/exe/png_to_hex +38 -0
- data/exe/ucisc +8 -5
- data/lib/micro_cisc.rb +30 -7
- data/lib/micro_cisc/compile/compiler.rb +10 -2
- data/lib/micro_cisc/compile/instruction.rb +40 -13
- data/lib/micro_cisc/compile/statement.rb +118 -34
- data/lib/micro_cisc/version.rb +1 -1
- data/lib/micro_cisc/vm/color_lcd_display.rb +115 -0
- data/lib/micro_cisc/vm/device.rb +32 -21
- data/lib/micro_cisc/vm/processor.rb +20 -9
- data/lib/micro_cisc/vm/term_device.rb +4 -8
- data/ucisc.gemspec +2 -0
- data/ucisc.vim +14 -8
- metadata +36 -4
- data/examples/image.ucisc +0 -543
- data/lib/micro_cisc/vm/video.rb +0 -151
data/examples/hello_world.ucisc
CHANGED
@@ -1,96 +1,87 @@
|
|
1
|
-
#
|
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/
|
4
|
+
# $ exe/ucisc examples/hello_world.ucisc core/stdlib.ucisc
|
11
5
|
#
|
12
|
-
#
|
13
|
-
# to view the result on the stack
|
6
|
+
# The stack should be empty when complete. It prints the following:
|
14
7
|
#
|
15
|
-
#
|
16
|
-
# Or use GitHub https://github.com/grokthis/ucisc-ruby#usage
|
8
|
+
# Hello, world! uCISC is here!
|
17
9
|
|
18
|
-
|
19
|
-
|
10
|
+
################
|
11
|
+
# Syntax Setup #
|
12
|
+
################
|
20
13
|
&pc as 0.reg
|
14
|
+
&banking as 4.reg
|
21
15
|
|
22
|
-
#
|
16
|
+
# Conditionals
|
23
17
|
$zero? as 0.eff
|
24
18
|
$not_zero? as 1.eff
|
25
19
|
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
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
|
-
|
36
|
-
|
26
|
+
################
|
27
|
+
# Code Entry #
|
28
|
+
################
|
29
|
+
&stack as copy 0.imm 1.reg
|
37
30
|
|
38
|
-
|
31
|
+
$stack[1] <= init_device(16.imm)
|
32
|
+
$&device_control = $stack
|
39
33
|
|
40
|
-
|
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
|
-
|
44
|
-
|
36
|
+
# pop device control
|
37
|
+
copy &&device_control 1.imm &stack
|
45
38
|
|
46
|
-
|
47
|
-
|
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
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
42
|
+
################
|
43
|
+
# Main Section #
|
44
|
+
################
|
45
|
+
hello:
|
46
|
+
% "\nHello, world! uCISC is here!\n\n"
|
57
47
|
|
58
|
-
|
59
|
-
|
48
|
+
print_string: # (&device_control, &string)
|
49
|
+
$&string, $&device_control, $&return = $stack
|
60
50
|
|
61
|
-
&
|
62
|
-
|
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
|
-
#
|
66
|
-
|
67
|
-
compute
|
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
|
-
|
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
|
-
|
75
|
-
|
76
|
-
|
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
|
-
|
68
|
+
$stack <= mem_copy(&string.data, &stdout, $word_len)
|
79
69
|
|
80
|
-
|
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
|
-
#
|
85
|
-
copy
|
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 $
|
80
|
+
copy $len $stdout.read_count
|
90
81
|
# Unbank all regions
|
91
|
-
copy 0.imm
|
92
|
-
|
93
|
-
copy &stack 4.imm &stack
|
82
|
+
copy 0.imm &banking
|
94
83
|
|
95
84
|
# Jump return
|
96
|
-
copy
|
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
|
+
|
data/exe/png_to_hex
ADDED
@@ -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
|
+
|