#!bin/sh # # Dependencies: # - mktemp (tempfile is enough, but in most distros it complains about being deprecated) # - xxd # - sed # #set -x ################################ # Configuration # Set to 1 to print stacks after each stack operation LOGSTACKS=0 # Set to 1 to print device operations LOGDEVICE=0 # Set to 1 to print vector start LOGVECTOR=0 ################################ # Logging # # Prints whatever is given, will be made configurable logprint() { echo "$@" >&2 } logerror() { logprint ERROR "$@" } loginfo() { logprint INFO "$@" } logdevice() { if [ $LOGDEVICE -eq 1 ] ; then logprint DEVICE "$@" fi } logvector() { if [ $LOGVECTOR -eq 1 ] ; then logprint VECTOR "$@" fi } ################################################################ # Block Memory Implementation ############################################################### # BMI Latch Register bmiltc= # # Gets given index from data block # $1 - index # $2.. - data block_get_val() { shift $1 shift bmiltc=$1 } # # Returns updated data block # $1 - index # $2 - value # $3.. - data block_set_val() { local idx val idx=$1 val=$2 shift 2 case $idx in 0) bmiltc="$val $2 $3 $4 $5 $6 $7 $8 $9 ${10} ${11} ${12} ${13} ${14} ${15} ${16}" ;; 1) bmiltc="$1 $val $3 $4 $5 $6 $7 $8 $9 ${10} ${11} ${12} ${13} ${14} ${15} ${16}" ;; 2) bmiltc="$1 $2 $val $4 $5 $6 $7 $8 $9 ${10} ${11} ${12} ${13} ${14} ${15} ${16}" ;; 3) bmiltc="$1 $2 $3 $val $5 $6 $7 $8 $9 ${10} ${11} ${12} ${13} ${14} ${15} ${16}" ;; 4) bmiltc="$1 $2 $3 $4 $val $6 $7 $8 $9 ${10} ${11} ${12} ${13} ${14} ${15} ${16}" ;; 5) bmiltc="$1 $2 $3 $4 $5 $val $7 $8 $9 ${10} ${11} ${12} ${13} ${14} ${15} ${16}" ;; 6) bmiltc="$1 $2 $3 $4 $5 $6 $val $8 $9 ${10} ${11} ${12} ${13} ${14} ${15} ${16}" ;; 7) bmiltc="$1 $2 $3 $4 $5 $6 $7 $val $9 ${10} ${11} ${12} ${13} ${14} ${15} ${16}" ;; 8) bmiltc="$1 $2 $3 $4 $5 $6 $7 $8 $val ${10} ${11} ${12} ${13} ${14} ${15} ${16}" ;; 9) bmiltc="$1 $2 $3 $4 $5 $6 $7 $8 $9 $val ${11} ${12} ${13} ${14} ${15} ${16}" ;; 10) bmiltc="$1 $2 $3 $4 $5 $6 $7 $8 $9 ${10} $val ${12} ${13} ${14} ${15} ${16}" ;; 11) bmiltc="$1 $2 $3 $4 $5 $6 $7 $8 $9 ${10} ${11} $val ${13} ${14} ${15} ${16}" ;; 12) bmiltc="$1 $2 $3 $4 $5 $6 $7 $8 $9 ${10} ${11} ${12} $val ${14} ${15} ${16}" ;; 13) bmiltc="$1 $2 $3 $4 $5 $6 $7 $8 $9 ${10} ${11} ${12} ${13} $val ${15} ${16}" ;; 14) bmiltc="$1 $2 $3 $4 $5 $6 $7 $8 $9 ${10} ${11} ${12} ${13} ${14} $val ${16}" ;; 15) bmiltc="$1 $2 $3 $4 $5 $6 $7 $8 $9 ${10} ${11} ${12} ${13} ${14} ${15} $val" ;; esac } # # Creates an empty block block_create() { bmiltc="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0" } # # Creates a memory backed by variables # $1 - variable name # $2 - number of blocks blockmem_init() { local idx eb eval "$1i=$2" block_create idx=0 while [ $idx -lt $2 ] ; do eval "$1$idx='$bmiltc'" idx=$(($idx + 1)) done } # # Prints the block memory matrix # $1 - name blockmem_print() { local nb idx echo "[Mem]$1" eval "nb=\$$1i" idx=0 while [ $idx -lt $nb ] ; do eval "echo \$$1$idx" idx=$(($idx + 1)) done } # # Updates a value in block memory by adjusting appropriate block # $1 - name # $2 - offset # $3 - value blockmem_write() { local val nb bn cb bo val=$(($3 % 256)) eval "nb=\$$1i" bn=$(($2 / 16)) if [ $bn -lt $nb ] ; then eval "cb=\$$1$bn" bo=$(($2 % 16)) block_set_val $bo $val $cb eval "$1$bn='$bmiltc'" fi } # # Reads a single value # $1 - name # $2 - offset blockmem_read() { local nb bn cb bo eval "nb=\$$1i" bn=$(($2 / 16)) if [ $bn -lt $nb -a $2 -ge 0 ] ; then eval "cb=\$$1$bn" bo=$(($2 % 16)) block_get_val $bo $cb else logerror blockmem_read $1 $2 $nb $bn fi } # End of Block Memory Implementation ################################################################ ################################ # Block Memory Areas blockmem_init dm 16 blockmem_init rs 16 blockmem_init ws 16 blockmem_init mb 4096 ################################ # Integer ALU # Global ALU Latch register alultc= # # Converts 4-bit number to hex # $1 - 0..15 nibble_to_hex() { case $1 in 10) alultc=a ;; 11) alultc=b ;; 12) alultc=c ;; 13) alultc=d ;; 14) alultc=e ;; 15) alultc=f ;; *) alultc=$1 ;; esac } # # Converts byte number 0..255 to hex 00..FF # $1 - 0..255 byte_to_hex() { local l h L l=$(($1 % 16)) h=$((( $1 / 16 ) % 16)) nibble_to_hex $l L=$alultc nibble_to_hex $h alultc=$alultc$L } # # Convers a 4-bit hex number to decimal # $1 - 0..F hex_to_nibble() { case $1 in A|a) alultc=10 ;; B|b) alultc=11 ;; C|c) alultc=12 ;; D|d) alultc=13 ;; E|e) alultc=14 ;; F|f) alultc=15 ;; *) alultc=$1 ;; esac } # # Converts hex number 00..FF (letters must be capitalized) to number 0..255 # $1 - 00..FF hex_to_byte() { local l h L l=${1#?} h=${1%?} hex_to_nibble $l L=$alultc hex_to_nibble $h alultc=$(($alultc * 16 + $L)) } # # Binary output output_byte() { byte_to_hex $1 echo $alultc | xxd -r -p } ################################ # Generic stacks # Stack data latch register stkltc= # # Reads byte at given stack address # $1 - stack number: 0 or 1 # $2 - address: 0..255 stk_read_byte() { case $1 in 0) blockmem_read ws $2 ;; 1) blockmem_read rs $2 ;; esac stkltc=$bmiltc } # # Writes byte at given stack address # $1 - stack number: 0 or 1 # $2 - address: 0..255 # $3 - byte: 0..255 stk_write_byte() { case $1 in 0) blockmem_write ws $2 $3 ;; 1) blockmem_write rs $2 $3 ;; esac } # Stack pointers and pointer latch RSTCUR=0 WSTCUR=0 stkptr= # # Gets given stack pointer # $1 - stack number: 0 or 1 stk_ptr_get() { case $1 in 0) stkptr=$WSTCUR ;; 1) stkptr=$RSTCUR ;; esac } # # Sets given stack pointer # $1 - stack number: 0 or 1 # $2 - new pointer: 0..255 stk_ptr_set() { case $1 in 0) WSTCUR=$(($2 % 256)) ;; 1) RSTCUR=$(($2 % 256)) ;; esac } # # Pushes a byte onto the stack # $1 - stack number: 0 or 1 # $2 - byte to push: 0..255 stk_push_byte() { local ptr nptr stk_ptr_get $1 ptr=$stkptr stk_write_byte $1 $ptr $2 nptr=$(($ptr + 1)) stk_ptr_set $1 $nptr } # # Pops a byte from the stack # $1 - stack number: 0 or 1 stk_pop_byte() { local ptr nptr stk_ptr_get $1 ptr=$stkptr nptr=$((( $ptr + 255 ) % 256)) stk_read_byte $1 $nptr stk_write_byte $1 $nptr 0 stk_ptr_set $1 $nptr } # # Gets a byte from the stack # $1 - stack number # $2 - offset (typically 0, 1, or 2) stk_get_byte() { local ptr stk_ptr_get $1 ptr=$stkptr stk_read_byte $1 $((( $ptr - $2 - 1 + 256 ) % 256)) } # # Gets a word from the stack, points at high byte, low byte is in the # +1 address further on the stack # $1 - stack number # $2 - offset (typically 0, 1, or 2) stk_get_word() { local l h la stk_get_byte $1 $2 h=$stkltc la=$(($2 + 1)) stk_get_byte $1 $la l=$stkltc stkltc=$((256 * $l + $h)) } # # Drops some values from given stack # $1 - stack number # $2 - number of bytes to drop stk_drop() { local idx idx=$2 while [ $idx -gt 0 ] ; do stk_pop_byte $1 idx=$(($idx - 1)) done } # # Prints stack for debugging # $1 - stack number: 0 or 1 stk_print() { local ptr cptr b hh stk_ptr_get $1 ptr=$stkptr cptr=$((( $ptr - 8 + 256 ) % 256)) while [ $cptr -ne $ptr ] ; do stk_read_byte $1 $cptr b=$stkltc byte_to_hex $b echo -n $alultc cptr=$((( $cptr + 1 ) % 256)) if [ $cptr -eq 0 ] ; then echo -n "|" else echo -n " " fi done echo -n "<" byte_to_hex $ptr echo $alultc } ################################ # WST and RST wrappers wst_push_byte() { stk_push_byte 0 $1 } wst_pop_byte() { stk_pop_byte 0 } rst_push_byte() { stk_push_byte 1 $1 } rst_pop_byte() { stk_pop_byte 1 echo $stkltc } # # Prints both stacks for debugging stks_print() { echo -n "WST " stk_print 0 16 echo -n "RST " stk_print 1 16 } # # Conditional logging log_debug_stacks() { if [ $LOGSTACKS -eq 1 ] ; then stks_print >&2 fi } ################################ # Stack words # # Pushes a word onto a stack, lower byte first # $1 - stack number: 0 or 1 # $2 - word: 0..65535 stk_push_word() { stk_push_byte $1 $(($2 / 256)) stk_push_byte $1 $(($2 % 256)) } # # Pops a word from the stack, lower byte first # $1 - stack number: 0 or 1 stk_pop_word() { local ptr nptr l h stk_ptr_get $1 ptr=$stkptr nptr=$((( $ptr + 255 ) % 256)) stk_read_byte $1 $nptr l=$stkltc stk_write_byte $1 $nptr 0 nptr=$((( $ptr + 254 ) % 256)) stk_read_byte $1 $nptr h=$stkltc stk_write_byte $1 $nptr 0 stkltc=$(($h * 256 + $l)) stk_ptr_set $1 $nptr } wst_push_word() { stk_push_word 0 $1 } rst_push_word() { stk_push_word 1 $1 } ################################ # ROM Support # # $1.. hex bytes rom_row_convert() { local num while [ -n "$1" ] ; do hex_to_byte $1 echo -n "$alultc " shift done echo } # # Loads ROM into current memory # $1 - path to ROM iamge rom_load() { local blk nums tmp tmp=`mktemp` blk=16 xxd -p -c 16 $1 | sed 's/../& /g' >$tmp while read row ; do nums=`rom_row_convert $row` eval "mb$blk='$nums'" blk=$(($blk + 1)) done <$tmp rm $tmp } ################################ # Memory support # # Reads a single byte from given bank # $1 - 0..65535 address # $2 - bank, 0 implicit mem_read_byte() { blockmem_read mb $1 } # # Writes a single byte to given bank # $1 - 0..65535 address # $2 - 0..255 byte to write # $3 - bank, 0 implicit mem_write_byte() { blockmem_write mb $1 $(($2 % 256)) } mem_read_word() { local h a mem_read_byte $1 $2 h=$bmiltc a=$((( $1 + 1 ) % 65536)) mem_read_byte $a $2 bmiltc=$(($h * 256 + $bmiltc)) } mem_write_word() { local l h a l=$(($2 % 256)) h=$(($2 / 256)) a=$((( $1 + 1 ) % 65536)) mem_write_byte $1 $h $3 mem_write_byte $a $l $3 } ################################ # CPU # # Sets the current Program Counter # $1 - new PC CPUPC=0 cpu_set_pc() { CPUPC=$(($1 % 65536)) } # # Resets the CPU by setting program counter to 256 cpu_reset() { cpu_set_pc 256 } # # Fetches next byte cpu_fetch_byte() { local pc pc=$CPUPC mem_read_byte $pc } cpu_post_fetch_byte() { pc=$CPUPC cpu_set_pc $((( $pc + 1 ) % 65536)) } # # Fetches a word (typically an immediate value) cpu_fetch_word() { local pc h l pc=$CPUPC mem_read_byte $pc h=$bmiltc pc=$((( $pc + 1 ) % 65536)) mem_read_byte $pc bmiltc=$(($h * 256 + $bmiltc)) } cpu_post_fetch_word() { pc=$CPUPC cpu_set_pc $((( $pc + 2 ) % 65536)) } # JSI cpu_jsi() { local pc ad pc=$CPUPC cpu_fetch_word ad=$bmiltc cpu_post_fetch_word rst_push_word $(($pc + 2)) cpu_set_pc $(($pc + $ad + 2)) # loginfo JSI to to $ad which is $(($pc + $ad)) } # JCI cpu_jci() { local ad cnd pc cpu_fetch_word ad=$bmiltc cpu_post_fetch_word wst_pop_byte cnd=$stkltc if [ $cnd -ne 0 ] ; then if [ $ad -gt 32767 ] ; then ad=$(($ad - 65536)) fi pc=$CPUPC cpu_set_pc $(($pc + $ad)) fi } # JMI cpu_jmi() { local ad pc cpu_fetch_word ad=$bmiltc cpu_post_fetch_word pc=$CPUPC # loginfo JMI ad is $ad if [ $ad -gt 32767 ] ; then ad=$(($ad - 65536)) fi cpu_set_pc $(($pc + $ad)) # loginfo JMI from $pc to $ad } # # Handles BRK opcode - which represents more instructions # $1 - K # $2 - R # $3 - 2 cpu_brk() { case $1$3 in 10) # LIT cpu_fetch_byte stk_push_byte $2 $bmiltc cpu_post_fetch_byte ;; 11) # LIT2 cpu_fetch_word stk_push_word $2 $bmiltc cpu_post_fetch_word ;; *) # rest case $2$3 in 00) # BRK running=0 ;; 01) # JCI cpu_jci ;; 10) # JMI cpu_jmi ;; 11) # JSI cpu_jsi ;; esac ;; esac } # # Prints the nul-terminated string # $1 - address in bank0 system_metadata() { local off byt off=$(($1 + 1)) byt=256 mem_read_byte $1 # loginfo "SYSTEM METADATA version" $bmiltc while [ $byt -ne 0 ] ; do mem_read_byte $off byt=$bmiltc if [ $byt -ne 0 ] ; then output_byte $byt fi off=$(($off + 1)) done echo } # # Handles device output message # $1 - port # $2 - value (byte or word) # $3 - if 1, word, if 0, byte device_output() { local tp if [ $3 -eq 1 ] ; then tp=word else tp=byte fi logdevice "DEO[$1]=$2 ($tp)" case $3 in 0) blockmem_write dm $1 $2 ;; 1) blockmem_write dm $1 $(($2 / 256)) blockmem_write dm $((( $1 + 1 ) % 256)) $(($2 % 256)) ;; esac case $1 in 6) # 06 System/metadata, expects word if [ $3 -eq 1 ] ; then system_metadata $2 >&2 fi ;; 15) # 0f System/exitcode if [ $2 -gt 0 ] ; then vmpower=0 vmexit=$(($2 % 128)) fi ;; 16) # Console/vector if [ $3 -eq 1 ] ; then convec=$2 fi ;; 24) # 18 Console/write output_byte $2 ;; 25) # 18 Console/error output_byte $2 >&2 ;; esac } # # Handles DEO opcode # $1 - K # $2 - R # $3 - 2 cpu_deo() { local prt val stk_get_byte $2 0 prt=$stkltc if [ $3 -eq 1 ] ; then stk_get_word $2 1 val=$stkltc else stk_get_byte $2 1 val=$stkltc fi device_output $prt $val $3 if [ $1 -ne 1 ] ; then if [ $3 -eq 1 ] ; then stk_drop $2 3 else stk_drop $2 2 fi fi } # # STH opcode / $1=K $2=R $3=2 cpu_sth() { local src dst val src=$2 dst=$((1 - $2)) case $3 in 0) # byte case $1 in 0) stk_pop_byte $src val=$stkltc ;; 1) stk_get_byte $src 0 val=$stkltc ;; esac stk_push_byte $dst $val ;; 1) # word case $1 in 0) stk_pop_word $src val=$stkltc ;; 1) stk_get_word $src 0 val=$stkltc ;; esac stk_push_word $dst $val ;; esac } # # DUP opcode: K R 2 cpu_dup() { local val case $3 in 0) stk_get_byte $2 0 val=$stkltc stk_push_byte $2 $val if [ $1 -eq 1 ] ; then stk_push_byte $2 $val fi ;; 1) stk_get_word $2 0 val=$stkltc stk_push_word $2 $val if [ $1 -eq 1 ] ; then stk_push_word $2 $val fi ;; esac } # # ADD: K R 2 cpu_add() { local a b case $1 in 0) # normal case $3 in 0) # byte stk_pop_byte $2 b=$stkltc stk_pop_byte $2 a=$stkltc ;; 1) # word stk_pop_word $2 b=$stkltc stk_pop_word $2 a=$stkltc ;; esac ;; 1) # keep case $3 in 0) # byte stk_get_byte $2 0 b=$stkltc stk_get_byte $2 1 a=$stkltc ;; 1) # word stk_get_word $2 0 b=$stkltc stk_get_word $2 2 a=$stkltc ;; esac ;; esac case $3 in 0) # byte stk_push_byte $2 $(($a + $b)) ;; 1) # word stk_push_word $2 $(($a + $b)) ;; esac } # POP: K R 2 cpu_pop() { case $1 in 0) case $3 in 0) stk_pop_byte $2 ;; 1) stk_pop_word $2 ;; esac ;; 1) # nop true ;; esac } # JMP: K R 2 cpu_jmp() { local ad pc case $3 in 0) # byte relative case $1 in 0) stk_pop_byte $2 ad=$stkltc ;; 1) stk_get_byte $2 0 ad=$stkltc ;; esac pc=$CPUPC if [ $ad -gt 127 ] ; then ad=$(($ad - 256)) fi ad=$(($pc + $ad)) ;; 1) # word absolute case $1 in 0) stk_pop_word $2 ad=$stkltc ;; 1) stk_get_word $2 0 ad=$stkltc ;; esac ;; esac cpu_set_pc $ad } # LDA: K R 2 cpu_lda() { local ad case $1 in 0) stk_pop_word $2 ad=$stkltc ;; 1) stk_get_word $2 0 ad=$stkltc ;; esac case $3 in 0) mem_read_byte $ad stk_push_byte $2 $bmiltc ;; 1) mem_read_word $ad stk_push_word $2 $bmiltc ;; esac } # INC: K R 2 cpu_inc() { local val # echo vvvvvvvv INC >&2 # stks_print >&2 case $1 in 0) case $3 in 0) stk_pop_byte $2 val=$stkltc ;; 1) stk_pop_word $2 val=$stkltc ;; esac ;; 1) case $3 in 0) stk_get_byte $2 0 val=$stkltc ;; 1) stk_get_word $2 0 val=$stkltc ;; esac ;; esac val=$(($val + 1)) case $3 in 0) stk_push_byte $2 $val ;; 1) stk_push_word $2 $val ;; esac # stks_print >&2 # echo ^^^^^^^^ >&2 } # JSR: K R 2 cpu_jsr() { local ws rs ad pc ws=$2 rs=$((1 - $2)) case $1 in 0) # normal case $3 in 0) stk_pop_byte $ws ad=$stkltc ;; 1) stk_pop_word $ws ad=$stkltc ;; esac ;; 1) # keep case $3 in 0) stk_get_byte $ws 0 ad=$stkltc ;; 1) stk_get_word $ws 0 ad=$stkltc ;; esac ;; esac pc=$CPUPC stk_push_word $rs $pc case $3 in 0) if [ $ad -gt 127 ] ; then ad=$(($ad - 256)) fi cpu_set_pc $(($ad + $pc)) ;; 1) cpu_set_pc $ad ;; esac } # LDR: K R 2 cpu_ldr() { local ad pc case $1 in 0) stk_pop_byte $2 ad=$stkltc ;; 1) stk_get_byte $2 0 ad=$stkltc ;; esac if [ $ad -gt 127 ] ; then ad=$(($ad - 256)) fi pc=$CPUPC # loginfo ======== LDR $1 $2 $3 $ad $pc ad=$(($pc + $ad)) case $3 in 0) mem_read_byte $ad stk_push_byte $2 $bmiltc ;; 1) mem_read_word $ad stk_push_word $2 $bmiltc ;; esac # stks_print >&2 } # SWP: K R 2 cpu_swp() { local a b case $1 in 0) # normal case $3 in 0) # byte stk_pop_byte $2 b=$stkltc stk_pop_byte $2 a=$stkltc ;; 1) # word stk_pop_word $2 b=$stkltc stk_pop_word $2 a=$stkltc ;; esac ;; 1) # keep case $3 in 0) # byte stk_get_byte $2 0 b=$stkltc stk_get_byte $2 1 a=$stkltc ;; 1) # word stk_get_word $2 0 b=$stkltc stk_get_word $2 2 a=$stkltc ;; esac ;; esac case $3 in 0) stk_push_byte $2 $b stk_push_byte $2 $a ;; 1) stk_push_word $2 $b stk_push_word $2 $a ;; esac } # STZ: K R 2 cpu_stz() { local ad val case $1 in 0) # normal stk_pop_byte $2 ad=$stkltc case $3 in 0) stk_pop_byte $2 val=$stkltc ;; 1) stk_pop_word $2 val=$stkltc ;; esac ;; 1) # keep stk_get_byte $2 0 ad=$stkltc case $3 in 0) stk_get_byte $2 1 val=$stkltc ;; 1) stk_get_word $2 1 val=$stkltc ;; esac ;; esac # loginfo ======== STZ $1 $2 $3 $ad $val case $3 in 0) mem_write_byte $ad $val ;; 1) mem_write_byte $ad $(($val / 256)) mem_write_byte $((( $ad + 1 ) % 256)) $(($val % 256)) ;; esac } # AND: K R 2 cpu_and() { local a b case $1$3 in 00) stk_pop_byte $2 b=$stkltc stk_pop_byte $2 a=$stkltc ;; 01) stk_pop_word $2 b=$stkltc stk_pop_word $2 a=$stkltc ;; 10) stk_get_byte $2 0 b=$stkltc stk_get_byte $2 1 a=$stkltc ;; 11) stk_get_word $2 0 b=$stkltc stk_get_word $2 2 a=$stkltc ;; esac case $3 in 0) stk_push_byte $2 $(($a & $b)) ;; 1) stk_push_word $2 $(($a & $b)) ;; esac } # SFT: K R 2 cpu_sft() { local val bth lft rgt res case $1 in 0) stk_pop_byte $2 bth=$stkltc case $3 in 0) stk_pop_byte $2 val=$stkltc ;; 1) stk_pop_word $2 val=$stkltc ;; esac ;; 1) stk_get_byte $2 0 bth=$stkltc case $3 in 0) stk_get_byte $2 1 val=$stkltc ;; 1) stk_get_word $2 1 val=$stkltc ;; esac ;; esac lft=$((( $bth / 16 ) % 16)) rgt=$(($bth % 16)) res=$((($val >> $rgt) << $lft)) case $3 in 0) stk_push_byte $2 $res ;; 1) stk_push_word $2 $res ;; esac # loginfo ================ SFT $val $bth $lft $rgt $res } # GTH: K R 2 cpu_gth() { local a b case $1 in 0) case $3 in 0) stk_pop_byte $2 b=$stkltc stk_pop_byte $2 a=$stkltc ;; 1) stk_pop_word $2 b=$stkltc stk_pop_word $2 a=$stkltc ;; esac ;; 1) case $3 in 0) stk_get_byte $2 0 b=$stkltc stk_get_byte $2 1 a=$stkltc ;; 1) stk_get_word $2 0 b=$stkltc stk_get_word $2 2 a=$stkltc ;; esac ;; esac if [ $a -gt $b ] ; then stk_push_byte $2 1 else stk_push_byte $2 0 fi } # MUL: K R 2 cpu_mul() { local a b case $1 in 0) case $3 in 0) stk_pop_byte $2 b=$stkltc stk_pop_byte $2 a=$stkltc ;; 1) stk_pop_word $2 b=$stkltc stk_pop_word $2 a=$stkltc ;; esac ;; 1) case $3 in 0) stk_get_byte $2 0 b=$stkltc stk_get_byte $2 1 a=$stkltc ;; 1) stk_get_word $2 0 b=$stkltc stk_get_word $2 2 a=$stkltc ;; esac ;; esac # loginfo ================ MUL $a $b case $3 in 0) stk_push_byte $2 $(($a * $b)) ;; 1) stk_push_word $2 $(($a * $b)) ;; esac } # EQU: K R 2 cpu_equ() { local a b case $1 in 0) case $3 in 0) stk_pop_byte $2 b=$stkltc stk_pop_byte $2 a=$stkltc ;; 1) stk_pop_word $2 b=$stkltc stk_pop_word $2 a=$stkltc ;; esac ;; 1) case $3 in 0) stk_get_byte $2 0 b=$stkltc stk_get_byte $2 1 a=$stkltc ;; 1) stk_get_word $2 0 b=$stkltc stk_get_word $2 2 a=$stkltc ;; esac ;; esac if [ $a -eq $b ] ; then stk_push_byte $2 1 else stk_push_byte $2 0 fi } # NIP: K R 2 cpu_nip() { local a b case $1 in 0) case $3 in 0) stk_pop_byte $2 b=$stkltc stk_pop_byte $2 a=$stkltc ;; 1) stk_pop_word $2 b=$stkltc stk_pop_word $2 a=$stkltc ;; esac ;; 1) case $3 in 0) stk_get_byte $2 0 b=$stkltc stk_get_byte $2 1 a=$stkltc ;; 1) stk_get_word $2 0 b=$stkltc stk_get_word $2 2 a=$stkltc ;; esac ;; esac case $3 in 0) stk_push_byte $2 $b ;; 1) stk_push_word $2 $b ;; esac } # DIV: K R 2 cpu_div() { local a b res case $1 in 0) case $3 in 0) stk_pop_byte $2 b=$stkltc stk_pop_byte $2 a=$stkltc ;; 1) stk_pop_word $2 b=$stkltc stk_pop_word $2 a=$stkltc ;; esac ;; 1) case $3 in 0) stk_get_byte $2 0 b=$stkltc stk_get_byte $2 1 a=$stkltc ;; 1) stk_get_word $2 0 b=$stkltc stk_get_word $2 2 a=$stkltc ;; esac ;; esac if [ $b -eq 0 ] ; then res=0 else res=$(($a / $b)) fi case $3 in 0) stk_push_byte $2 $res ;; 1) stk_push_word $2 $res ;; esac } # ROT: K R 2 cpu_rot() { local a b c case $1 in 0) case $3 in 0) stk_pop_byte $2 c=$stkltc stk_pop_byte $2 b=$stkltc stk_pop_byte $2 a=$stkltc ;; 1) stk_pop_word $2 c=$stkltc stk_pop_word $2 b=$stkltc stk_pop_word $2 a=$stkltc ;; esac ;; 1) case $3 in 0) stk_get_byte $2 0 c=$stkltc stk_get_byte $2 1 b=$stkltc stk_get_byte $2 2 a=$stkltc ;; 1) stk_get_word $2 0 c=$stkltc stk_get_word $2 2 b=$stkltc stk_get_word $2 4 a=$stkltc ;; esac ;; esac case $3 in 0) stk_push_byte $2 $b stk_push_byte $2 $c stk_push_byte $2 $a ;; 1) stk_push_word $2 $b stk_push_word $2 $c stk_push_word $2 $a ;; esac } # OVR: K R 2 cpu_ovr() { local a b case $1 in 0) case $3 in 0) stk_pop_byte $2 b=$stkltc stk_pop_byte $2 a=$stkltc ;; 1) stk_pop_word $2 b=$stkltc stk_pop_word $2 a=$stkltc ;; esac ;; 1) case $3 in 0) stk_get_byte $2 0 b=$stkltc stk_get_byte $2 1 a=$stkltc ;; 1) stk_get_word $2 0 b=$stkltc stk_get_word $2 2 a=$stkltc ;; esac ;; esac case $3 in 0) stk_push_byte $2 $a stk_push_byte $2 $b stk_push_byte $2 $a ;; 1) stk_push_word $2 $a stk_push_word $2 $b stk_push_word $2 $a ;; esac } # NEQ: K R 2 cpu_neq() { local a b case $1 in 0) case $3 in 0) stk_pop_byte $2 b=$stkltc stk_pop_byte $2 a=$stkltc ;; 1) stk_pop_word $2 b=$stkltc stk_pop_word $2 a=$stkltc ;; esac ;; 1) case $3 in 0) stk_get_byte $2 0 b=$stkltc stk_get_byte $2 1 a=$stkltc ;; 1) stk_get_word $2 0 b=$stkltc stk_get_word $2 2 a=$stkltc ;; esac ;; esac if [ $a -ne $b ] ; then stk_push_byte $2 1 else stk_push_byte $2 0 fi } # LTH: K R 2 cpu_lth() { local a b case $1 in 0) case $3 in 0) stk_pop_byte $2 b=$stkltc stk_pop_byte $2 a=$stkltc ;; 1) stk_pop_word $2 b=$stkltc stk_pop_word $2 a=$stkltc ;; esac ;; 1) case $3 in 0) stk_get_byte $2 0 b=$stkltc stk_get_byte $2 1 a=$stkltc ;; 1) stk_get_word $2 0 b=$stkltc stk_get_word $2 2 a=$stkltc ;; esac ;; esac if [ $a -lt $b ] ; then stk_push_byte $2 1 else stk_push_byte $2 0 fi } # JCN: K R 2 cpu_jcn() { local ad cnd pc case $1 in 0) case $3 in 0) stk_pop_byte $2 ad=$stkltc if [ $ad -gt 127 ] ; then ad=$(($ad - 128)) fi stk_pop_byte $2 cnd=$stkltc ;; 1) stk_pop_word $2 ad=$stkltc stk_pop_byte $2 cnd=$stkltc ;; esac ;; 1) case $3 in 0) stk_get_byte $2 0 ad=$stkltc if [ $ad -gt 127 ] ; then ad=$(($ad - 128)) fi stk_get_byte $2 1 cnd=$stkltc ;; 1) stk_get_word $2 0 ad=$stkltc stk_get_byte $2 2 cnd=$stkltc ;; esac ;; esac if [ $cnd -ne 0 ] ; then case $3 in 0) pc=$CPUPC cpu_set_pc $(($pc + $ad)) ;; 1) cpu_set_pc $ad ;; esac fi } # LDZ: K R 2 cpu_ldz() { local ad h l case $1 in 0) stk_pop_byte $2 ad=$stkltc ;; 1) stk_get_byte $2 0 ad=$stkltc ;; esac # loginfo ======== LDZ $1 $2 $3 $ad case $3 in 0) mem_read_byte $ad stk_push_byte $2 $bmiltc ;; 1) mem_read_byte $ad h=$bmiltc ad=$((( $ad + 1 ) % 256)) mem_read_byte $ad l=$bmiltc stk_push_word $2 $(($h * 256 + $l)) ;; esac } # STR: K R 2 cpu_str() { local ad val pc case $1 in 0) stk_pop_byte $2 ad=$stkltc case $3 in 0) stk_pop_byte $2 val=$stkltc ;; 1) stk_pop_word $2 val=$stkltc ;; esac ;; 1) stk_get_byte $2 0 ad=$stkltc case $3 in 0) stk_get_byte $2 1 val=$stkltc ;; 1) stk_get_word $2 1 val=$stkltc ;; esac ;; esac if [ $ad -gt 127 ] ; then ad=$(($ad - 256)) fi pc=$CPUPC # loginfo STR $1 $2 $3 $ad $pc $val case $3 in 0) mem_write_byte $(($pc + $ad)) $val ;; 1) mem_write_word $(($pc + $ad)) $val ;; esac } # STA: K R 2 cpu_sta() { local ad val case $1 in 0) stk_pop_word $2 ad=$stkltc case $3 in 0) stk_pop_byte $2 val=$stkltc ;; 1) stk_pop_word $2 val=$stkltc ;; esac ;; 1) stk_get_word $2 0 ad=$stkltc case $3 in 0) stk_get_byte $2 2 val=$stkltc ;; 1) stk_get_word $2 2 val=$stkltc ;; esac ;; esac case $3 in 0) mem_write_byte $ad $val ;; 1) mem_write_word $ad $val ;; esac } # SUB: K R 2 cpu_sub() { local a b res case $1 in 0) # normal case $3 in 0) # byte stk_pop_byte $2 b=$stkltc stk_pop_byte $2 a=$stkltc ;; 1) # word stk_pop_word $2 b=$stkltc stk_pop_word $2 a=$stkltc ;; esac ;; 1) # keep case $3 in 0) # byte stk_get_byte $2 0 b=$stkltc stk_get_byte $2 1 a=$stkltc ;; 1) # word stk_get_word $2 0 b=$stkltc stk_get_word $2 2 a=$stkltc ;; esac ;; esac res=$(($a - $b)) if [ $res -lt 0 ] ; then res=$((65536 + $res)) fi # loginfo ======== SUB $1 $2 $3 $a $b $res case $3 in 0) # byte stk_push_byte $2 $res ;; 1) # word stk_push_word $2 $res ;; esac } # ORA: K R 2 cpu_ora() { local a b case $1 in 0) case $3 in 0) stk_pop_byte $2 b=$stkltc stk_pop_byte $2 a=$stkltc ;; 1) stk_pop_word $2 b=$stkltc stk_pop_word $2 a=$stkltc ;; esac ;; 1) case $3 in 0) stk_get_byte $2 0 b=$stkltc stk_get_byte $2 1 a=$stkltc ;; 1) stk_get_word $2 0 b=$stkltc stk_get_word $2 2 a=$stkltc ;; esac ;; esac case $3 in 0) stk_push_byte $2 $(($a | $b)) ;; 1) stk_push_word $2 $(($a | $b)) ;; esac } # EOR: K R 2 cpu_eor() { local a b case $1 in 0) case $3 in 0) stk_pop_byte $2 b=$stkltc stk_pop_byte $2 a=$stkltc ;; 1) stk_pop_word $2 b=$stkltc stk_pop_word $2 a=$stkltc ;; esac ;; 1) case $3 in 0) stk_get_byte $2 0 b=$stkltc stk_get_byte $2 1 a=$stkltc ;; 1) stk_get_word $2 0 b=$stkltc stk_get_word $2 2 a=$stkltc ;; esac ;; esac case $3 in 0) stk_push_byte $2 $(($a ^ $b)) ;; 1) stk_push_word $2 $(($a ^ $b)) ;; esac } # DEI: K R 2 cpu_dei() { local prt num case $1 in 0) stk_pop_byte $2 prt=$stkltc ;; 1) stk_get_byte $2 0 prt=$stkltc ;; esac case $3 in 0) blockmem_read dm $prt num=$bmiltc stk_push_byte $2 $bmiltc logdevice "DEI[$prt]=$num ($3)" ;; 1) blockmem_read dm $prt num=$bmiltc stk_push_byte $2 $bmiltc prt=$((( $prt + 1 ) % 256)) blockmem_read dm $prt stk_push_byte $2 $bmiltc logdevice "DEI[$prt]=$num ($3)" ;; esac # stks_print >&2 } # # Decodes given instruction # $1 - instruction number: 0..255 cpu_decode() { local opc flk flr fl2 opc=$(($1 % 32)) fl2=$((( $1 / 32 ) % 2)) flr=$((( $1 / 64 ) % 2)) flk=$((( $1 / 128 ) % 2)) # logprefix $flk $flr $fl2 case $opc in 0) cpu_brk $flk $flr $fl2 ;; 1) cpu_inc $flk $flr $fl2 ;; 2) cpu_pop $flk $flr $fl2 ;; 3) cpu_nip $flk $flr $fl2 ;; 4) cpu_swp $flk $flr $fl2 ;; 5) cpu_rot $flk $flr $fl2 ;; 6) cpu_dup $flk $flr $fl2 ;; 7) cpu_ovr $flk $flr $fl2 ;; 8) cpu_equ $flk $flr $fl2 ;; 9) cpu_neq $flk $flr $fl2 ;; 10) cpu_gth $flk $flr $fl2 ;; 11) cpu_lth $flk $flr $fl2 ;; 12) cpu_jmp $flk $flr $fl2 ;; 13) cpu_jcn $flk $flr $fl2 ;; 14) cpu_jsr $flk $flr $fl2 ;; 15) cpu_sth $flk $flr $fl2 ;; 16) cpu_ldz $flk $flr $fl2 ;; 17) cpu_stz $flk $flr $fl2 ;; 18) cpu_ldr $flk $flr $fl2 ;; 19) cpu_str $flk $flr $fl2 ;; 20) cpu_lda $flk $flr $fl2 ;; 21) cpu_sta $flk $flr $fl2 ;; 22) cpu_dei $flk $flr $fl2 ;; 23) cpu_deo $flk $flr $fl2 ;; 24) cpu_add $flk $flr $fl2 ;; 25) cpu_sub $flk $flr $fl2 ;; 26) cpu_mul $flk $flr $fl2 ;; 27) cpu_div $flk $flr $fl2 ;; 28) cpu_and $flk $flr $fl2 ;; 29) cpu_ora $flk $flr $fl2 ;; 30) cpu_eor $flk $flr $fl2 ;; 31) cpu_sft $flk $flr $fl2 ;; esac } # # Runs the CPU loop cpu_run() { local opc running=1 while [ $running -eq 1 ] ; do cpu_fetch_byte opc=$bmiltc cpu_post_fetch_byte cpu_decode $opc done } # # Construct cmdline stream argsa= parse_arg() { echo "$@" | xxd -p | sed 's/../& /g' } parse_cmdline() { local parg while [ -n "$1" ] ; do parg=`parse_arg $1` argsa="$argsa$parg" shift done } # # Sends a single argument byte to the console vector # $1.. - argsa send_arg_byte() { hex_to_byte $1 # loginfo arg $1 $alultc blockmem_write dm 18 $alultc if [ $alultc -eq 10 ] ; then blockmem_write dm 23 3 else blockmem_write dm 23 2 fi shift if [ -z "$2" ] ; then shift fi argsa="$*" } ################################ # Main if [ -z "$1" ] ; then echo "Usage: $0 image.rom args" exit 1 fi rom_load $1 shift parse_cmdline "$@" logvector boot cpu_reset vmpower=1 vmexit=128 convec=0 if [ -z "$argsa" ] ; then wargs=0 blockmem_write dm 23 0 else wargs=1 blockmem_write dm 23 1 fi cpu_run # boot vector logvector /boot while [ $vmpower -eq 1 ] ; do # Try command-line arguments first if [ -n "$argsa" ] ; then # echo "argsa: $argsa" send_arg_byte $argsa else # Maybe end of arguments if [ $wargs -eq 1 ] ; then # echo wargs wargs=0 blockmem_write dm 23 4 blockmem_write dm 18 10 else # Then console # echo console inch=`dd bs=1c count=1 status=none 2>/dev/null|xxd -p` if [ -n "$inch" ] ; then blockmem_write dm 23 1 hex_to_byte $inch # echo converted $inch to $alultc >&2 blockmem_write dm 18 $alultc else # echo finishing >&2 blockmem_write dm 23 4 fi fi fi if [ $convec -gt 0 ] ; then logvector console cpu_set_pc $convec cpu_run # stks_print >&2 # blockmem_print mb >&2 logvector /console fi sleep 0.0167 done exit $vmexit