aboutsummaryrefslogtreecommitdiff
path: root/tclasm.tcl
blob: d326c005eafcf94d311221acb4e06ad185c59734 (plain)
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
#!/bin/grep this[ ]script
# this script is to be sourced, not executed by itself

# procedures starting with "__" are internal and not to be used in asm code;
# procedures starting with "_" are generalized procedures, that are not meant
# to be directly used for code translated from webasm;
# the rest are typical procedures webasm code will be mapped to

proc __parse_binary {binary} {
    set value 0

    for {set bits_remaining $binary} \
	{"$bits_remaining" != ""} \
	{set bits_remaining [string range $bits_remaining 1 end]} {
	    set value [expr $value * 2]

	    if [string match 1* $bits_remaining] {
		incr value
	    } elseif [string match 0* $bits_remaining] {
		# nothing
	    } else {
		error "'$binary' cannot be parsed as a binary number"
	    }
	}

    return $value
}

proc __parse_number {number} {
    if [string match h?* $number] {
	set value 0x[string range $number 1 end]
    } elseif [string match b?* $number] {
	set value [__parse_binary [string range $number 1 end]]
    } elseif [string match {d[0123456789]?*} $number] {
	set value [string range $number 1 end]
    } elseif [string match {-[0123456789]*} $number] {
	set value $number
    } elseif [string match {[0123456789]*} $number] {
	set value $number
    } else {
	error "'$number' is not a properly formatted number"
    }

    return $value
}

proc __to_binary {number length {can_be_negative -neg_ok}} {
    if [string equal $can_be_negative -neg_ok] {
	set can_be_negative 1
    } elseif [string equal $can_be_negative -no_neg] {
	set can_be_negative 0
    } else {
	error "'$can_be_negative' is not a valid value for the\
               'can_be_negative' argument"
    }

    set value [__parse_number $number]

    if {$value < 0 && !$can_be_negative} {
	error "value '$number' provided where negative values are not allowed"
    }

    if {($can_be_negative && $value >= 2 ** ($length - 1)) ||
	$value >= 2 ** $length ||
	$value < -(2 ** ($length - 1))} {
	error "value '$number' doesn't fit into $length\
	       bits[expr $can_be_negative ? "{ with sign}" : "{}"]"
    }

    for {set result ""} {$length > 0} {incr length -1} {
	set result [expr $value % 2]$result
	set value [expr $value >> 1]
    }

    return $result
}

# example: __encode_immediate im+=213
#          __encode_immediate im<<=hAB
#          __encode_immediate im=im
#          __encode_immediate im=-33    # negative value only allowed for im=
#          __encode_immediate im<<=b1101101
proc __encode_immediate {im_modification} {
    if [string equal $im_modification im=im] {
	return 000000000000
    } elseif [string match im<<=?* $im_modification] {
	return 1[__to_binary [string range $im_modification 5 end] 11 -no_neg]
    } elseif [string match im=?* $im_modification] {
	return 01[__to_binary [string range $im_modification 3 end] 10]
    } elseif [string match im+=?* $im_modification] {
	return 001[__to_binary [string range $im_modification 4 end] 9 -no_neg]
    } else {
	error "'$im_modification' is not a valid im modification"
    }
}

# example: __encode_access_type load
#          __encode_access_type store
proc __encode_access_type {type} {
    if {"$type" == "load"} {
	return 1
    } elseif {"$type" == "store"} {
	return 0
    } else {
	error "'$type' is not a valid memory access type"
    }
}

# example: __encode_addressing @im
#          __encode_addressing @im+sp
proc __encode_addressing {address} {
    if {"$address" == "@im"} {
	return 1
    } elseif {"$address" == "@im+sp"} {
	return 0
    } else {
	error "'$address' is not a valid addressing"
    }
}

# example: __encode_reg r0
#          __encode_reg r1
proc __encode_reg {register} {
    if {"$register" == "r0"} {
	return 1
    } elseif {"$register" == "r1"} {
	return 0
    } else {
	error "'$register' is not a valid register"
    }
v}

# example: __encode_memory_access load r0 @im im=d1840
#          __encode_memory_access store r1 @im+sp im<<=b1100011
proc __encode_memory_access {type register address {im_modification im=im}} {
    set type [__encode_access_type $type]
    set address [__encode_addressing $address]
    set register [__encode_reg $register]
    set im_modification [__encode_immediate $im_modification]
    return 1$type$address$register$im_modification    
}

# example: __encode_swap swap
#          __encode_swap no_swap
proc __encode_swap {swap_regs} {
    if {"$swap_regs" == "swap"} {
	return 1
    } elseif {"$swap_regs" == "no_swap"} {
	return 0
    } else {
	error "got '$swap_regs' where 'swap' or 'no_swap' was expected"
    }
}

# example: __encode_swap swap
#          __encode_swap no_swap
proc __encode_cond {cond} {
    if {"$cond" == "cond"} {
	return 1
    } elseif {"$cond" == "non-cond"} {
	return 0
    } else {
	error "got '$cond' where 'cond' or 'non-cond' was expected"
    }
}

# example: __encode_extended_instruction halt
#          __encode_extended_instruction nop
proc __encode_extended_instruction {instruction} {
    if {"$instruction" == "nop"} {
	return [__to_binary 0 9]
    } elseif {"$instruction" == "halt"} {
	return [__to_binary 1 9]
    } elseif {"$instruction" == "set_sp"} {
	return [__to_binary 2 9]
    } elseif {"$instruction" == "add"} {
	return [__to_binary 3 9]
    } elseif {"$instruction" == "sub"} {
	return [__to_binary 4 9]
    } elseif {"$instruction" == "div"} {
	return [__to_binary 5 9]
    } elseif {"$instruction" == "mul"} {
	return [__to_binary 6 9]
    } else {
	error "no such extended instruction: '$instruction'"
    }
}

# example: _load r0 @im im+=2   # negative values are *not* allowed for im+=
#          _load r1 @im im=im
#          _load r0 @im+sp      # im=im is the default if not specified
#          _load r0 @im im<<=12 # 12 is the value shifted, shift is always by 11
#          _load r1 @im im=-9   # negative values are only allowed for im=
#          _load r0 @im+sp im=0 # this results in using only sp for addressing
proc _load {register address {im_modification im=im}} {
    puts [__encode_memory_access load $register $address $im_modification]
}

# example: _store r0 @im im+=2   # same semantics as _load
#          _store r1 @im im=im
#          _store r0 @im+sp
#          _store r0 @im im<<=12
#          _store r1 @im im=-9
#          _store r0 @im+sp im=0
proc _store {register address {im_modification im=im}} {
    puts [__encode_memory_access store $register $address $im_modification]
}

# example: _jump im<<=b1101011 cond swap
#          _jump im=im non-cond
proc _jump {{im_modification im=im} {cond non-cond} {swap_regs no_swap}} {
    set swap_regs [__encode_swap $swap_regs]
    set cond [__encode_cond $cond]
    puts 01$cond$swap_regs[__encode_immediate $im_modification]
}

# example: _cond_jump im=h16A swap
proc _cond_jump {{im_modification im=im} {swap_regs no_swap}} {
    _jump $im_modification cond $swap_regs
}

# example: _extended_instruction nop swap  # it's really no longer a true nop...
#          _extended_instruction halt
proc _extended_instruction {instruction {swap_regs no_swap}} {
    set swap_regs [__encode_swap $swap_regs]
    set no_im_modification 000
    set instruction [__encode_extended_instruction $instruction]
    puts 000$swap_regs$no_im_modification$instruction
}

# example: _immediate im+=4 swap
#          _immediate im<<=hFF
#          _immediate im=im      # this one gives the same bit result as 'nop'
proc _immediate {im_modification {swap_regs no_swap}} {
    set swap_regs [__encode_swap $swap_regs]
    set im_modification [__encode_immediate $im_modification]
    puts 000$swap_regs$im_modification
}

# example: _exchange_im im<<=b10101010101 swap
proc _exchange_im {{im_modification im=im} {swap_regs no_swap}} {
    set swap_regs [__encode_swap $swap_regs]
    set im_modification [__encode_immediate $im_modification]
    puts 001$swap_regs$im_modification
}

proc _const {command number} {

    set value [expr [__parse_number $number] + 0]
    if {$value < 2 ** 9 && $value >= -(2 ** 9)} {
	$command im=$value
    } elseif {$value < 2 ** 20 && $value >= -(2 ** 20)} {
	_immediate im=[expr $value >> 11]
	$command im<<=[expr $value & 0x7ff]
    } elseif {$value < 2 ** 32 && $value >= -(2 ** 31)} {
	# remove sign
	set value [expr $value & (2 ** 32 - 1)]

	_immediate im<<=[expr $value >> 22]
	_immediate im<<=[expr ($value >> 11) & 0x7ff]
	$command im<<=[expr $value & 0x7ff]
    } else {
	error "number '$number' doesn't fit in 32 bits"
    }
}

# example: stack up
#          stack down
proc stack {direction} {
    if {"$direction" == "up"} {
	_load r0 @im+sp im=4
    } elseif {"$direction" == "down"} {
	_store r0 @im+sp im=0
    } else {
	error "bad opcode: stack $direction"
    }
}

# example: store im<<=00000000000
proc store {{im_modification im=im}} {
    _store r1 @im $im_modification
}

# example: load im=hDD
proc load {{im_modification im=im}} {
    _load r1 @im $im_modification
}

# example: jump        # if no address is given - im is used
#          jump h4FFFE
proc jump {{address im_address}} {
    if {"$address" == "im_address"} {
	_jump im=im
    } else {
	_const _jump $address
    }
}

# example: cond_jump 
#          cond_jump b100100110100111101 # same semantics as 'jump'
proc cond_jump {{address im_address}} {
    if {"$address" == "im_address"} {
	_jump im=im cond
    } else {
	_const _cond_jump $address
    }
}

foreach instruction {halt nop swap add sub div mul} {
    proc $instruction {} "
        _extended_instruction $instruction
    "
}

proc swap {} {
    _extended_instruction nop swap
}


# example: exchange_im im+=15
proc exchange_im {{im_modification im=im}} {
    _exchange_im $im_modification
}

# example: const -100249
#          const hDEADBEEF # automatically translates to multiple instructions
proc const {number} {
    _const _exchange_im $number
}

# example: immediate b11101101010000010100010 # analogous to 'const' above
proc immediate {number} {
    _const _immediate $number
}

# example: set_sp h2FFFE # analogous to 'const' and 'immediate' above
proc set_sp {number} {
    immediate $number
    _extended_instruction set_sp
}

# example: store@ h57574
proc store@ {address_number} {
    _const store $address_number
}

# example: load@ h20000
proc load@ {address_number} {
    _const load $address_number
}