aboutsummaryrefslogtreecommitdiff
#!/usr/bin/tclsh

# procedures starting with "__" are internal and not to be used in asm code;
# procedures starting with "_" are low-level 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} {
    set value [__parse_number $number]

    if {$value >= 2 ** $length ||
	$value < -(2 ** ($length - 1))} {
	error "value '$number' doesn't fit into $length bits"
    }

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

    return $result
}


# _im and _with_im are needed to construct higher-level
# multiinstructions, that use an immediate value

proc _im {value} {
    puts 1[__to_binary $value 15]
}

proc _with_im {command number} {

    set value [expr [__parse_number $number] + 0]
    if {$value < 2 ** 6 && $value >= -(2 ** 6)} {
	$command $value
    } elseif {$value < 2 ** 21 && $value >= -(2 ** 21)} {
	_im [expr $value >> 7]
	$command [expr $value & 0x7F]
    } elseif {$value < 2 ** 32 && $value >= -(2 ** 31)} {
	_im [expr $value >> 22]
	_im [expr ($value >> 7) & 0x7FFF]
	$command [expr $value & 0x7F]
    } else {
	error "number '$number' doesn't fit in 32 bits"
    }
}


# Load and store instructions, different variants */

proc _store {address_part} {
    puts 011111100[__to_binary $address_part 7]
}

proc store {address} {
    _with_im _store $address
}


proc _store+ {address_part} {
    puts 011011100[__to_binary $address_part 7]
}

proc store+ {address} {
    _with_im _store+ $address
}


proc _storeb {address_part} {
    puts 011111000[__to_binary $address_part 7]
}

proc storeb {address} {
    _with_im _storeb $address
}


proc _storeb+ {address_part} {
    puts 011011000[__to_binary $address_part 7]
}

proc storeb+ {address} {
    _with_im _storeb+ $address
}


proc _storew {address_part} {
    puts 011111010[__to_binary $address_part 7]
}

proc storew {address} {
    _with_im _storew $address
}


proc _storew+ {address_part} {
    puts 011011010[__to_binary $address_part 7]
}

proc storew+ {address} {
    _with_im _storew+ $address
}


proc _load {address_part} {
    puts 010111100[__to_binary $address_part 7]
}

proc load {address} {
    _with_im _load $address
}


proc _load+ {address_part} {
    puts 010011100[__to_binary $address_part 7]
}

proc load+ {address} {
    _with_im _load+ $address
}


proc _loadbzx {address_part} {
    puts 010111000[__to_binary $address_part 7]
}

proc loadbzx {address} {
    _with_im _loadbzx $address
}


proc _loadbzx+ {address_part} {
    puts 010011000[__to_binary $address_part 7]
}

proc loadbzx+ {address} {
    _with_im _loadbzx+ $address
}


proc _loadbsx {address_part} {
    puts 010111001[__to_binary $address_part 7]
}

proc loadbsx {address} {
    _with_im _loadbsx $address
}


proc _loadbsx+ {address_part} {
    puts 010011001[__to_binary $address_part 7]
}

proc loadbsx+ {address} {
    _with_im _loadbsx+ $address
}


proc _loadwzx {address_part} {
    puts 010111010[__to_binary $address_part 7]
}

proc loadwzx {address} {
    _with_im _loadwzx $address
}


proc _loadwzx+ {address_part} {
    puts 010011010[__to_binary $address_part 7]
}

proc loadwzx+ {address} {
    _with_im _loadwzx+ $address
}


proc _loadwsx {address_part} {
    puts 010111011[__to_binary $address_part 7]
}

proc loadwsx {address} {
    _with_im _loadwsx $address
}


proc _loadwsx+ {address_part} {
    puts 010011011[__to_binary $address_part 7]
}

proc loadwsx+ {address} {
    _with_im _loadwsx+ $address
}


# Instructions, that do not change stack size

proc halt {} {
    puts 0000000000000000
}


proc nop {} {
    puts 0000000000000001
}


proc swap {} {
    puts 0000000000000010
}


proc _set_sp {address_part} {
    puts 010000000[__to_binary $address_part 7]
}

proc set_sp {address} {
    _with_im _set_sp $address
}


proc _jump {address_part} {
    puts 010000001[__to_binary $address_part 7]
}

proc jump {address} {
    _with_im _jump $address
}


proc _add_sp {number_part} {
    puts 010000010[__to_binary $number_part 7]
}

proc add_sp {number} {
    _with_im _add_sp $number
}


# Instructions, that grow stack

proc tee {} {
    puts 0001000000000000
}


proc get_frame {} {
    puts 0001000000000001
}


proc _const {value_part} {
    puts 010100000[__to_binary $value_part 7]
}

proc const {value} {
    _with_im _const $value
}


proc _call {address_part} {
    puts 010100001[__to_binary $address_part 7]
}

proc call {address} {
    _with_im _call $address
}


# Instructions, that shrink stack

proc add {} {
    puts 0011000000000000
}


proc sub {} {
    puts 0011000000000001
}


proc div {} {
    puts 0011000000000010
}


proc mul {} {
    puts 0011000000000011
}


proc drop {} {
    puts 0011000000000100
}


proc eq {} {
    puts 0011000000000111
}


proc lt {} {
    puts 0011000000001000
}


proc ult {} {
    puts 0011000000001001
}


proc le {} {
    puts 0011000000001010
}


proc ule {} {
    puts 0011000000001011
}


proc gt {} {
    puts 0011000000001100
}


proc ugt {} {
    puts 0011000000001101
}


proc ge {} {
    puts 0011000000001110
}


proc uge {} {
    puts 0011000000001111
}

proc rem {} {
    puts 0011000000010000
}


proc ret {} {
    puts 0011000010000000
}


proc _cond_jump {address_part} {
    puts 011100001[__to_binary $address_part 7]
}

proc cond_jump {address} {
    _with_im _cond_jump $address
}


proc _cond_jump_n {address_part} {
    puts 011100010[__to_binary $address_part 7]
}

proc cond_jump_n {address} {
    _with_im _cond_jump_n $address
}


# translate instructions in the file given as command line argument
source [lindex $argv 0]