| #!/bin/sh |
| |
| # Copyright 2021-2025 Rivoreo |
| |
| # This Source Code Form is subject to the terms of the Mozilla Public License, |
| # v. 2.0. If a copy of the MPL was not distributed with this file, You can |
| # obtain one at https://mozilla.org/MPL/2.0/. |
| |
| |
| if [ $# != 1 ]; then |
| printf "Usage: %s <program>\\n" "$0" |
| exit 255 |
| fi |
| |
| DISK0_FILE=A |
| DISK1_FILE=B |
| |
| [ -n "$I8080ASH_PRINT_REGISTERS_ON_HALT" ] && PRINT_REGISTERS_ON_HALT=1 |
| [ -z "$BASH" ] && [ -n "$I8080ASH_ASSUME_BASH" ] && BASH=assumed |
| if [ -n "$I8080ASH_USE_BASH_READ" ] && [ -z "$BASH" ]; then |
| echo "Warning: I8080ASH_USE_BASH_READ is set, but current shell is not bash" 1>&2 |
| fi |
| |
| set -e |
| |
| stdin_status_flags= |
| |
| set_stdin_nonblock() { |
| [ -n "$I8080ASH_ASSUME_NONBLOCK_STDIN" ] && return |
| if [ -z "$stdin_status_flags" ]; then |
| stdin_status_flags="`perl -e 'use strict; use POSIX; print STDOUT fcntl(STDIN, F_GETFL, 0);'`" || return |
| stdin_status_flags="${stdin_status_flags%% *}" |
| printf %s\\n "$stdin_status_flags" | grep -Eq '^[0-9]+$' || return |
| fi |
| perl -e "use strict; use POSIX; fcntl(STDIN, F_SETFL, $stdin_status_flags | O_NONBLOCK);" |
| } |
| |
| restore_stdin_status_flags() { |
| [ -z "$stdin_status_flags" ] && return |
| perl -e "use strict; use POSIX; fcntl(STDIN, F_SETFL, $stdin_status_flags);" |
| } |
| |
| # Lookup table for parity, zero and sign flags |
| # Updates external variable 'PZS' |
| get_pzs_flags() { |
| local i=$1 |
| set -- \ |
| 0x44 0x00 0x00 0x04 0x00 0x04 0x04 0x00 0x00 0x04 0x04 0x00 0x04 0x00 0x00 0x04 \ |
| 0x00 0x04 0x04 0x00 0x04 0x00 0x00 0x04 0x04 0x00 0x00 0x04 0x00 0x04 0x04 0x00 \ |
| 0x00 0x04 0x04 0x00 0x04 0x00 0x00 0x04 0x04 0x00 0x00 0x04 0x00 0x04 0x04 0x00 \ |
| 0x04 0x00 0x00 0x04 0x00 0x04 0x04 0x00 0x00 0x04 0x04 0x00 0x04 0x00 0x00 0x04 \ |
| 0x00 0x04 0x04 0x00 0x04 0x00 0x00 0x04 0x04 0x00 0x00 0x04 0x00 0x04 0x04 0x00 \ |
| 0x04 0x00 0x00 0x04 0x00 0x04 0x04 0x00 0x00 0x04 0x04 0x00 0x04 0x00 0x00 0x04 \ |
| 0x04 0x00 0x00 0x04 0x00 0x04 0x04 0x00 0x00 0x04 0x04 0x00 0x04 0x00 0x00 0x04 \ |
| 0x00 0x04 0x04 0x00 0x04 0x00 0x00 0x04 0x04 0x00 0x00 0x04 0x00 0x04 0x04 0x00 \ |
| 0x80 0x84 0x84 0x80 0x84 0x80 0x80 0x84 0x84 0x80 0x80 0x84 0x80 0x84 0x84 0x80 \ |
| 0x84 0x80 0x80 0x84 0x80 0x84 0x84 0x80 0x80 0x84 0x84 0x80 0x84 0x80 0x80 0x84 \ |
| 0x84 0x80 0x80 0x84 0x80 0x84 0x84 0x80 0x80 0x84 0x84 0x80 0x84 0x80 0x80 0x84 \ |
| 0x80 0x84 0x84 0x80 0x84 0x80 0x80 0x84 0x84 0x80 0x80 0x84 0x80 0x84 0x84 0x80 \ |
| 0x84 0x80 0x80 0x84 0x80 0x84 0x84 0x80 0x80 0x84 0x84 0x80 0x84 0x80 0x80 0x84 \ |
| 0x80 0x84 0x84 0x80 0x84 0x80 0x80 0x84 0x84 0x80 0x80 0x84 0x80 0x84 0x84 0x80 \ |
| 0x80 0x84 0x84 0x80 0x84 0x80 0x80 0x84 0x84 0x80 0x80 0x84 0x80 0x84 0x84 0x80 \ |
| 0x84 0x80 0x80 0x84 0x80 0x84 0x84 0x80 0x80 0x84 0x84 0x80 0x84 0x80 0x80 0x84 |
| eval "PZS=\${$((i+1))}" |
| } |
| |
| if [ "`printf '' | hexdump -v -e '/1 \"%02x\"' 2> /dev/null`" = 03 ]; then |
| read_memory_image_in_hex() { |
| hexdump -v -e '/1 " %02x"' -n 65537 "$1" |
| } |
| read_disk_block_in_hex() { |
| hexdump -v -e '/1 " %02x"' -s $(($2*128)) -n 128 "$1" |
| } |
| byte_to_dec() { |
| hexdump -v -e '/1 "%u"' |
| } |
| else |
| read_memory_image_in_hex() { |
| xxd -c 256 -g 1 -l 65537 "$1" | sed -E -e "s/^[0-9a-f]+: //" -e "s/ .+//" |
| } |
| read_disk_block_in_hex() { |
| xxd -c 128 -g 1 -s $(($2*128)) -l 128 "$1" | sed -E -e 's/^[0-9a-f]+: //' -e 's/ .+//' |
| } |
| byte_to_dec() { |
| local hex |
| hex="`xxd -g 1 -l 1`" && [ -n "$hex" ] || return |
| hex="${hex#*: }" |
| hex="${hex%% *}" |
| echo $((0x$hex)) |
| } |
| fi |
| |
| read_key() { |
| if [ -n "$I8080ASH_USE_BASH_READ" ] && [ -n "$BASH" ]; then |
| local timeout variable r |
| if [ "$1" = --check ]; then |
| timeout=0 |
| variable=variable |
| else |
| timeout=0.5 |
| variable=key |
| # bash behaves erroneously if a read command is |
| # interrupted by a signal and the signal handler runs |
| # another read command; workaround this bug by |
| # deferring the handling of SIGINT |
| got_sigint= |
| trap got_sigint=1 INT |
| fi |
| IFS= read -d "" -r -s -n 1 -t $timeout $variable |
| r=$? |
| if [ "$1" != --check ]; then |
| trap sigint_handler INT |
| if [ $r != 0 ]; then |
| [ -n "$got_sigint" ] && sigint_handler |
| return $r |
| fi |
| [ "$key" = " |
| " ] && key=13 || key=`printf %u "'$key"` |
| fi |
| return $r |
| fi |
| # Must use dd(1) to limit read size to 1 byte, otherwise hexdump(1) or |
| # xxd(1) will read ahead of the size specified by '-n' or '-l', |
| # potentially causing key strikes to loss |
| local extended_dd_operands= |
| [ -n "$I8080ASH_USE_DD_IFLAG_NONBLOCK" ] && extended_dd_operands=iflag=nonblock |
| key="`dd bs=1 count=1 $extended_dd_operands 2> /dev/null | byte_to_dec`" || return |
| case "$key" in |
| "") |
| false |
| ;; |
| 10) |
| key=13 |
| ;; |
| esac |
| } |
| |
| read_disk() { |
| local f b |
| case $1 in |
| 0) f="$DISK0_FILE" ;; |
| 1) f="$DISK1_FILE" ;; |
| *) return 1 ;; |
| esac |
| [ -n "$f" ] || return |
| [ -f "$f" ] || return |
| local i=$2 |
| trap "" INT |
| for b in `read_disk_block_in_hex "$f" $3`; do |
| eval "M_$i=$((0x$b))" |
| i=$((i+1)) |
| [ $i -gt 65535 ] && break |
| done |
| trap sigint_handler INT |
| } |
| |
| write_disk() { |
| local f b |
| case $1 in |
| 0) f="$DISK0_FILE" ;; |
| 1) f="$DISK1_FILE" ;; |
| *) return 1 ;; |
| esac |
| [ -n "$f" ] || return |
| local i=$2 |
| local end=$((i+128)) |
| [ $end -gt 65535 ] && end=65535 |
| trap "" INT |
| while [ $i -lt $end ]; do |
| eval "b=\$M_$i" |
| printf "\\`printf %03o $b`" |
| i=$((i+1)) |
| done | dd ibs=1 "of=$f" obs=128 seek=$3 count=128 conv=notrunc > /dev/null 2>&1 |
| trap sigint_handler INT |
| } |
| |
| print_registers() { |
| local raw= hex= format |
| for a in "$@"; do case "$a" in |
| --raw) raw=1 ;; |
| --hex) hex=1 ;; |
| esac done |
| if [ -n "$raw" ]; then |
| if [ -n "$hex" ]; then |
| format="A=0x%02x B=0x%02x C=0x%02x D=0x%02x E=0x%02x H=0x%02x L=0x%02x SP=0x%04x PC=0x%04x FLAGS=0x%02x\\n" |
| else |
| format="A=%s B=%s C=%s D=%s E=%s H=%s L=%s SP=%s PC=%s FLAGS=%s\\n" |
| fi |
| printf "$format" $A $B $C $D $E $H $L $SP $PC $FLAGS 1>&2 |
| else |
| if [ -n "$hex" ]; then |
| format="A=0x%02x B=0x%02x C=0x%02x D=0x%02x E=0x%02x H=0x%02x L=0x%02x SP=0x%04x PC=0x%04x CARRY=%s PARITY=%s AUXCARRY=%s ZERO=%s SIGN=%s\\n" |
| else |
| format="A=%s B=%s C=%s D=%s E=%s H=%s L=%s SP=%s PC=%s CARRY=%s PARITY=%s AUXCARRY=%s ZERO=%s SIGN=%s\\n" |
| fi |
| printf "$format" $A $B $C $D $E $H $L $SP $PC $((FLAGS&1)) $((!!(FLAGS&(1<<2)))) $((!!(FLAGS&(1<<4)))) $((!!(FLAGS&(1<<6)))) $((!!(FLAGS&(1<<7)))) 1>&2 |
| fi |
| } |
| |
| dump_memory() { |
| local f b i=0 |
| [ -t 0 ] && stty icanon echo && trap - EXIT |
| set -e |
| if [ -n "$1" ] |
| then f="$1" |
| else while true; do |
| if ! read -p "File name? " f || [ -z "$f" ]; then |
| echo "Dump aborted" 1>&2 |
| #exit 1 |
| return 1 |
| fi |
| # Allow dumping to devices, named pipes, etc. |
| if [ -d "$f" ] || [ -f "$f" ]; then |
| printf "File '%s' already exists, please use another name\\n" "$f" 1>&2 |
| else |
| break |
| fi |
| done fi |
| printf "Dumping to '%s'... 0%%" "$f" 1>&2 |
| while [ $i -lt 65536 ]; do |
| eval "b=\$M_$i" |
| printf "\\`printf %03o $b`" |
| i=$((i+1)) |
| [ $((i&0xff)) = 0 ] && printf '\b\b\b\b%3u%%' $((i*100/65536)) 1>&2 |
| done > "$f" |
| echo 1>&2 |
| } |
| |
| sigint_handler() { |
| local answer |
| restore_stdin_status_flags |
| cat 1>&2 << EOT |
| Ctrl-C pressed, select an action: |
| 1. Pass ^C into emulator as keyboard input. |
| 2. Print register values. |
| 3. Print register values, dump memory into a file and terminate emulator. |
| 4. Terminate emulator. |
| EOT |
| while true; do |
| if [ -n "$I8080ASH_USE_BASH_READ" ] && [ -n "$BASH" ]; then |
| read -n 1 -p "> " answer |
| else |
| printf "> " 1>&2 |
| #answer="`dd bs=1 count=1 2> /dev/null`" || exit |
| answer="`perl -e 'my \$b; my \$s = sysread STDIN, \$b, 1; exit 1 if \$s < 1; print STDOUT \$b;'`" |
| fi || exit |
| printf %s\\n "$answer" 1>&2 |
| case "$answer" in |
| 1) |
| key=3 |
| break |
| ;; |
| 2) |
| print_registers --hex |
| break |
| ;; |
| 3) |
| trap - INT |
| print_registers |
| dump_memory |
| exit 0 |
| ;; |
| 4) |
| exit 0 |
| ;; |
| *) |
| echo "Invalid answer, please try again" 1>&2 |
| ;; |
| esac |
| done |
| [ -z "$BASH" ] || [ -z "$I8080ASH_USE_BASH_READ" ] && set_stdin_nonblock |
| } |
| |
| echo "i8080 emulator for Almquist shell" |
| echo "Copyright 2021-2025 Rivoreo" |
| |
| bytes="`read_memory_image_in_hex \"$1\"`" |
| [ -n "$bytes" ] |
| i=0 |
| for b in $bytes; do |
| if [ $i -gt 65535 ]; then |
| printf "Warning: Only first 64 KiB of '%s' is loaded\\n" "$1" 1>&2 |
| break |
| fi |
| eval "M_$i=$((0x$b))" |
| i=$((i+1)) |
| done |
| printf 'Loaded %s bytes\n' $i |
| while [ $i -lt 65536 ]; do |
| eval "M_$i=0" |
| i=$((i+1)) |
| done |
| unset i b |
| |
| for register in A B C D E H L SP PC FLAGS; do |
| eval "v=\"\$$register\"" |
| [ -n "$v" ] && v=$((v)) && [ "$v" -ge 0 ] && [ "$v" -lt $((${#register}>1?65536:256)) ] || v=0 |
| eval "$register=\$v" |
| done |
| unset register v |
| |
| i=0 |
| for flag in CARRY "" PARITY "" AUXCARRY "" ZERO SIGN; do |
| if [ -n "$flag" ] && eval "v=\"\$$flag\"" && [ -n "$v" ]; then |
| [ $((v)) -gt 0 ] && FLAGS=$((FLAGS|(1<<i))) || FLAGS=$((FLAGS&~(1<<i)&65535)) |
| fi |
| i=$((i+1)) |
| done |
| unset i flag v |
| |
| # Pending input key |
| key= |
| |
| set +e |
| |
| echo |
| if { [ -z "$BASH" ] || [ -z "$I8080ASH_USE_BASH_READ" ]; } && ! set_stdin_nonblock; then |
| echo "Failed to make stdin non-blocking" 1>&2 |
| exit 1 |
| fi |
| if [ -t 0 ]; then |
| if [ -n "$I8080ASH_USE_STTY_MIN" ]; then |
| trap "restore_stdin_status_flags; stty icanon echo sane" EXIT |
| stty -icanon -echo min 1 |
| else |
| trap "restore_stdin_status_flags; stty icanon echo" EXIT |
| stty -icanon -echo |
| fi |
| else |
| trap restore_stdin_status_flags EXIT |
| fi |
| trap "print_registers; dump_memory memory-dump; exit" HUP |
| trap sigint_handler INT |
| |
| do_add() { |
| local v=$((A+$1)) |
| FLAGS=$(((v>>8&1)|(((A&0xf)+($1&0xf))&0x10))) |
| A=$((v&255)) |
| get_pzs_flags $A |
| FLAGS=$((FLAGS|PZS)) |
| } |
| |
| do_adc() { |
| local v=$((A+$1+(FLAGS&1))) |
| FLAGS=$(((v>>8&1)|(((A&0xf)+($1&0xf)+(FLAGS&1))&0x10))) |
| A=$((v&255)) |
| get_pzs_flags $A |
| FLAGS=$((FLAGS|PZS)) |
| } |
| |
| do_sub() { |
| local v=$((A-$1)) |
| FLAGS=$(((v>>8&1)|(((A&0xf)-($1&0xf))&0x10))) |
| A=$((v&255)) |
| get_pzs_flags $A |
| FLAGS=$((FLAGS|PZS)) |
| } |
| |
| do_sbb() { |
| local v=$((A-$1-(FLAGS&1))) |
| FLAGS=$(((v>>8&1)|(((A&0xf)-($1&0xf)-(FLAGS&1))&0x10))) |
| A=$((v&255)) |
| get_pzs_flags $A |
| FLAGS=$((FLAGS|PZS)) |
| } |
| |
| do_ana() { |
| FLAGS=$((((A|$1)&8)<<1)) |
| A=$((A&$1)) |
| get_pzs_flags $A |
| FLAGS=$((FLAGS|PZS)) |
| } |
| |
| do_ret() { |
| local low_pc high_pc |
| eval "low_pc=\$M_$SP" |
| SP=$(((SP+1)&65535)) |
| eval "high_pc=\$M_$SP" |
| SP=$((SP+1)) |
| PC=$((low_pc|(high_pc<<8))) |
| } |
| |
| while true; do |
| eval "opcode=\$M_$PC" |
| PC=$((PC+1)) |
| case $opcode in |
| 0) # NOP |
| ;; |
| |
| 64) # MOV B,B |
| ;; |
| 65) # MOV B,C |
| B=$C |
| ;; |
| 66) # MOV B,D |
| B=$D |
| ;; |
| 67) # MOV B,E |
| B=$E |
| ;; |
| 68) # MOV B,H |
| B=$H |
| ;; |
| 69) # MOV B,L |
| B=$L |
| ;; |
| 70) # MOV B,M |
| eval "B=\$M_$((H<<8|L))" |
| ;; |
| 71) # MOV B,A |
| B=$A |
| ;; |
| |
| 72) # MOV C,B |
| C=$B |
| ;; |
| 73) # MOV C,C |
| ;; |
| 74) # MOV C,D |
| C=$D |
| ;; |
| 75) # MOV C,E |
| C=$E |
| ;; |
| 76) # MOV C,H |
| C=$H |
| ;; |
| 77) # MOV C,L |
| C=$L |
| ;; |
| 78) # MOV C,M |
| eval "C=\$M_$((H<<8|L))" |
| ;; |
| 79) # MOV C,A |
| C=$A |
| ;; |
| |
| 80) # MOV D,B |
| D=$B |
| ;; |
| 81) # MOV D,C |
| D=$C |
| ;; |
| 82) # MOV D,D |
| ;; |
| 83) # MOV D,E |
| D=$E |
| ;; |
| 84) # MOV D,H |
| D=$H |
| ;; |
| 85) # MOV D,L |
| D=$L |
| ;; |
| 86) # MOV D,M |
| eval "D=\$M_$((H<<8|L))" |
| ;; |
| 87) # MOV D,A |
| D=$A |
| ;; |
| |
| 88) # MOV E,B |
| E=$B |
| ;; |
| 89) # MOV E,C |
| E=$C |
| ;; |
| 90) # MOV E,D |
| E=$D |
| ;; |
| 91) # MOV E,E |
| ;; |
| 92) # MOV E,H |
| E=$H |
| ;; |
| 93) # MOV E,L |
| E=$L |
| ;; |
| 94) # MOV E,M |
| eval "E=\$M_$((H<<8|L))" |
| ;; |
| 95) # MOV E,A |
| E=$A |
| ;; |
| |
| 96) # MOV H,B |
| H=$B |
| ;; |
| 97) # MOV H,C |
| H=$C |
| ;; |
| 98) # MOV H,D |
| H=$D |
| ;; |
| 99) # MOV H,E |
| H=$E |
| ;; |
| 100) # MOV H,H |
| ;; |
| 101) # MOV H,L |
| H=$L |
| ;; |
| 102) # MOV H,M |
| eval "H=\$M_$((H<<8|L))" |
| ;; |
| 103) # MOV H,A |
| H=$A |
| ;; |
| |
| 104) # MOV L,B |
| L=$B |
| ;; |
| 105) # MOV L,C |
| L=$C |
| ;; |
| 106) # MOV L,D |
| L=$D |
| ;; |
| 107) # MOV L,E |
| L=$E |
| ;; |
| 108) # MOV L,H |
| L=$H |
| ;; |
| 109) # MOV L,L |
| ;; |
| 110) # MOV L,M |
| eval "L=\$M_$((H<<8|L))" |
| ;; |
| 111) # MOV L,A |
| L=$A |
| ;; |
| |
| 112) # MOV M,B |
| eval "M_$((H<<8|L))=\$B" |
| ;; |
| 113) # MOV M,C |
| eval "M_$((H<<8|L))=\$C" |
| ;; |
| 114) # MOV M,D |
| eval "M_$((H<<8|L))=\$D" |
| ;; |
| 115) # MOV M,E |
| eval "M_$((H<<8|L))=\$E" |
| ;; |
| 116) # MOV M,H |
| eval "M_$((H<<8|L))=\$H" |
| ;; |
| 117) # MOV M,L |
| eval "M_$((H<<8|L))=\$L" |
| ;; |
| |
| 118) # HLT |
| [ -n "$PRINT_REGISTERS_ON_HALT" ] && print_registers --hex |
| break |
| ;; |
| |
| 119) # MOV M,A |
| eval "M_$((H<<8|L))=\$A" |
| ;; |
| |
| 120) # MOV A,B |
| A=$B |
| ;; |
| 121) # MOV A,C |
| A=$C |
| ;; |
| 122) # MOV A,D |
| A=$D |
| ;; |
| 123) # MOV A,E |
| A=$E |
| ;; |
| 124) # MOV A,H |
| A=$H |
| ;; |
| 125) # MOV A,L |
| A=$L |
| ;; |
| 126) # MOV A,M |
| eval "A=\$M_$((H<<8|L))" |
| ;; |
| 127) # MOV A,A |
| ;; |
| |
| # MVI instructions (Move Immediate) |
| 6) # MVI B,nn |
| eval "B=\$M_$PC" |
| PC=$((PC+1)) |
| ;; |
| 14) # MVI C,nn |
| eval "C=\$M_$PC" |
| PC=$((PC+1)) |
| ;; |
| 22) # MVI D,nn |
| eval "D=\$M_$PC" |
| PC=$((PC+1)) |
| ;; |
| 30) # MVI E,nn |
| eval "E=\$M_$PC" |
| PC=$((PC+1)) |
| ;; |
| 38) # MVI H,nn |
| eval "H=\$M_$PC" |
| PC=$((PC+1)) |
| ;; |
| 46) # MVI L,nn |
| eval "L=\$M_$PC" |
| PC=$((PC+1)) |
| ;; |
| 54) # MVI M,nn |
| eval "_temp=\$M_$PC" |
| PC=$((PC+1)) |
| eval "M_$((H<<8|L))=\$_temp" |
| ;; |
| 62) # MVI A,nn |
| eval "A=\$M_$PC" |
| PC=$((PC+1)) |
| ;; |
| |
| # LXI instructions (Load immediate 16-bit) |
| 1) # LXI B,nnnn |
| eval "C=\$M_$PC" |
| PC=$(((PC+1)&65535)) |
| eval "B=\$M_$PC" |
| PC=$((PC+1)) |
| ;; |
| 17) # LXI D,nnnn |
| eval "E=\$M_$PC" |
| PC=$(((PC+1)&65535)) |
| eval "D=\$M_$PC" |
| PC=$((PC+1)) |
| ;; |
| 33) # LXI H,nnnn |
| eval "L=\$M_$PC" |
| PC=$(((PC+1)&65535)) |
| eval "H=\$M_$PC" |
| PC=$((PC+1)) |
| ;; |
| 49) # LXI SP,nnnn |
| eval "_Z=\$M_$PC" |
| PC=$(((PC+1)&65535)) |
| eval "_Y=\$M_$PC" |
| PC=$((PC+1)) |
| SP=$((_Z|(_Y<<8))) |
| ;; |
| |
| 128) # ADD B |
| do_add $B |
| ;; |
| 129) # ADD C |
| do_add $C |
| ;; |
| 130) # ADD D |
| do_add $D |
| ;; |
| 131) # ADD E |
| do_add $E |
| ;; |
| 132) # ADD H |
| do_add $H |
| ;; |
| 133) # ADD L |
| do_add $L |
| ;; |
| 134) # ADD M |
| eval "do_add \$M_$((H<<8|L))" |
| ;; |
| 135) # ADD A |
| do_add $A |
| ;; |
| |
| 136) # ADC B |
| do_adc $B |
| ;; |
| 137) # ADC C |
| do_adc $C |
| ;; |
| 138) # ADC D |
| do_adc $D |
| ;; |
| 139) # ADC E |
| do_adc $E |
| ;; |
| 140) # ADC H |
| do_adc $H |
| ;; |
| 141) # ADC L |
| do_adc $L |
| ;; |
| 142) # ADC M |
| eval "do_adc \$M_$((H<<8|L))" |
| ;; |
| 143) # ADC A |
| do_adc $A |
| ;; |
| |
| 144) # SUB B |
| do_sub $B |
| ;; |
| 145) # SUB C |
| do_sub $C |
| ;; |
| 146) # SUB D |
| do_sub $D |
| ;; |
| 147) # SUB E |
| do_sub $E |
| ;; |
| 148) # SUB H |
| do_sub $H |
| ;; |
| 149) # SUB L |
| do_sub $L |
| ;; |
| 150) # SUB M |
| eval "do_sub \$M_$((H<<8|L))" |
| ;; |
| 151) # SUB A |
| do_sub $A |
| ;; |
| |
| 152) # SBB B |
| do_sbb $B |
| ;; |
| 153) # SBB C |
| do_sbb $C |
| ;; |
| 154) # SBB D |
| do_sbb $D |
| ;; |
| 155) # SBB E |
| do_sbb $E |
| ;; |
| 156) # SBB H |
| do_sbb $H |
| ;; |
| 157) # SBB L |
| do_sbb $L |
| ;; |
| 158) # SBB M |
| eval "do_sbb \$M_$((H<<8|L))" |
| ;; |
| 159) # SBB A |
| do_sbb $A |
| ;; |
| |
| 198) # ADI nn |
| eval "_temp=\$M_$PC"; PC=$((PC+1)); _R=$((A+_temp)) |
| get_pzs_flags $((_R&255)) |
| FLAGS=$(((_R>>8&1)|PZS|(_R>>4&16))) |
| A=$((_R&255)) |
| ;; |
| 206) # ACI nn |
| eval "_temp=\$M_$PC"; PC=$((PC+1)); _R=$((A+_temp+(FLAGS&1))) |
| get_pzs_flags $((_R&255)) |
| FLAGS=$(((_R>>8&1)|PZS|(_R>>4&16))) |
| A=$((_R&255)) |
| ;; |
| 214) # SUI nn |
| eval "_temp=\$M_$PC"; PC=$((PC+1)); _R=$((A-_temp)) |
| get_pzs_flags $((_R&255)) |
| FLAGS=$(((_R>>8&1)|PZS|(_R>>4&16))) |
| A=$((_R&255)) |
| ;; |
| 222) # SBI nn |
| eval "_temp=\$M_$PC"; PC=$((PC+1)); _R=$((A-_temp-(FLAGS&1))) |
| get_pzs_flags $((_R&255)) |
| FLAGS=$(((_R>>8&1)|PZS|(_R>>4&16))) |
| A=$((_R&255)) |
| ;; |
| |
| 160) # ANA B |
| do_ana $B |
| ;; |
| 161) # ANA C |
| do_ana $C |
| ;; |
| 162) # ANA D |
| do_ana $D |
| ;; |
| 163) # ANA E |
| do_ana $E |
| ;; |
| 164) # ANA H |
| do_ana $H |
| ;; |
| 165) # ANA L |
| do_ana $L |
| ;; |
| 166) # ANA M |
| eval "do_ana \$M_$((H<<8|L))" |
| ;; |
| 167) # ANA A |
| do_ana $A |
| ;; |
| |
| 168) # XRA B |
| A=$((A^B)) |
| get_pzs_flags $A |
| FLAGS=$PZS |
| ;; |
| 169) # XRA C |
| A=$((A^C)) |
| get_pzs_flags $A |
| FLAGS=$PZS |
| ;; |
| 170) # XRA D |
| A=$((A^D)) |
| get_pzs_flags $A |
| FLAGS=$PZS |
| ;; |
| 171) # XRA E |
| A=$((A^E)) |
| get_pzs_flags $A |
| FLAGS=$PZS |
| ;; |
| 172) # XRA H |
| A=$((A^H)) |
| get_pzs_flags $A |
| FLAGS=$PZS |
| ;; |
| 173) # XRA L |
| A=$((A^L)) |
| get_pzs_flags $A |
| FLAGS=$PZS |
| ;; |
| 174) # XRA M |
| eval "_temp=\$M_$((H<<8|L))"; A=$((A^_temp)) |
| get_pzs_flags $A |
| FLAGS=$PZS |
| ;; |
| 175) # XRA A |
| A=0 |
| get_pzs_flags $A |
| FLAGS=$PZS |
| ;; |
| |
| 176) # ORA B |
| A=$((A|B)) |
| get_pzs_flags $A |
| FLAGS=$PZS |
| ;; |
| 177) # ORA C |
| A=$((A|C)) |
| get_pzs_flags $A |
| FLAGS=$PZS |
| ;; |
| 178) # ORA D |
| A=$((A|D)) |
| get_pzs_flags $A |
| FLAGS=$PZS |
| ;; |
| 179) # ORA E |
| A=$((A|E)) |
| get_pzs_flags $A |
| FLAGS=$PZS |
| ;; |
| 180) # ORA H |
| A=$((A|H)) |
| get_pzs_flags $A |
| FLAGS=$PZS |
| ;; |
| 181) # ORA L |
| A=$((A|L)) |
| get_pzs_flags $A |
| FLAGS=$PZS |
| ;; |
| 182) # ORA M |
| eval "_temp=\$M_$((H<<8|L))"; A=$((A|_temp)) |
| get_pzs_flags $A |
| FLAGS=$PZS |
| ;; |
| 183) # ORA A |
| get_pzs_flags $A |
| FLAGS=$PZS |
| ;; |
| |
| 184) # CMP B |
| v=$((A-B)) |
| get_pzs_flags $((v&255)) |
| FLAGS=$(((v>>8&1)|PZS|(((A&0xf)-(B&0xf))&16))) |
| ;; |
| 185) # CMP C |
| v=$((A-C)) |
| get_pzs_flags $((v&255)) |
| FLAGS=$(((v>>8&1)|PZS|(((A&0xf)-(C&0xf))&16))) |
| ;; |
| 186) # CMP D |
| v=$((A-D)) |
| get_pzs_flags $((v&255)) |
| FLAGS=$(((v>>8&1)|PZS|(((A&0xf)-(D&0xf))&16))) |
| ;; |
| 187) # CMP E |
| v=$((A-E)) |
| get_pzs_flags $((v&255)) |
| FLAGS=$(((v>>8&1)|PZS|(((A&0xf)-(E&0xf))&16))) |
| ;; |
| 188) # CMP H |
| v=$((A-H)) |
| get_pzs_flags $((v&255)) |
| FLAGS=$(((v>>8&1)|PZS|(((A&0xf)-(H&0xf))&16))) |
| ;; |
| 189) # CMP L |
| v=$((A-L)) |
| get_pzs_flags $((v&255)) |
| FLAGS=$(((v>>8&1)|PZS|(((A&0xf)-(L&0xf))&16))) |
| ;; |
| 190) # CMP M |
| eval "v1=\$M_$((H<<8|L))" |
| v2=$((A-v1)) |
| get_pzs_flags $((v2&255)) |
| FLAGS=$(((v2>>8&1)|PZS|(((A&0xf)-(v1&0xf))&16))) |
| ;; |
| 191) # CMP A |
| get_pzs_flags 0 |
| FLAGS=$PZS |
| ;; |
| |
| # Immediate logical instructions |
| 230) # ANI nn |
| eval "_temp=\$M_$PC"; PC=$((PC+1)); A=$((A&_temp)) |
| get_pzs_flags $A |
| FLAGS=$PZS |
| ;; |
| 238) # XRI nn |
| eval "_temp=\$M_$PC"; PC=$((PC+1)); A=$((A^_temp)) |
| get_pzs_flags $A |
| FLAGS=$PZS |
| ;; |
| 246) # ORI nn |
| eval "_temp=\$M_$PC"; PC=$((PC+1)); A=$((A|_temp)) |
| get_pzs_flags $A |
| FLAGS=$PZS |
| ;; |
| 254) # CPI nn |
| eval "_temp=\$M_$PC"; PC=$((PC+1)); _R=$((A-_temp)) |
| get_pzs_flags $((_R&255)) |
| FLAGS=$(((_R>>8&1)|PZS|(_R>>4&16))) |
| ;; |
| |
| 204) # CZ nnnn (Call if Zero) |
| eval "_Z=\$M_$PC"; PC=$(((PC+1)&65535)) |
| eval "_Y=\$M_$PC"; PC=$((PC+1)) |
| if [ $((FLAGS&64)) != 0 ]; then |
| SP=$((SP-1&65535)); eval "M_$SP=$((PC>>8))" |
| SP=$((SP-1&65535)); eval "M_$SP=$((PC&255))" |
| PC=$((_Z|(_Y<<8))) |
| fi |
| ;; |
| 212) # CNC nnnn (Call if No Carry) |
| eval "_Z=\$M_$PC"; PC=$((PC+1)) |
| eval "_Y=\$M_$PC"; PC=$((PC+1)) |
| if [ $((FLAGS&1)) = 0 ]; then |
| SP=$((SP-1&65535)); eval "M_$SP=$((PC>>8))" |
| SP=$((SP-1&65535)); eval "M_$SP=$((PC&255))" |
| PC=$((_Z|(_Y<<8))) |
| fi |
| ;; |
| 220) # CC nnnn (Call if Carry) |
| eval "_Z=\$M_$PC"; PC=$(((PC+1)&65535)) |
| eval "_Y=\$M_$PC"; PC=$((PC+1)) |
| if [ $((FLAGS&1)) != 0 ]; then |
| SP=$((SP-1&65535)); eval "M_$SP=$((PC>>8))" |
| SP=$((SP-1&65535)); eval "M_$SP=$((PC&255))" |
| PC=$((_Z|(_Y<<8))) |
| fi |
| ;; |
| 228) # CPO nnnn (Call if Parity Odd) |
| eval "_Z=\$M_$PC"; PC=$(((PC+1)&65535)) |
| eval "_Y=\$M_$PC"; PC=$((PC+1)) |
| if [ $((FLAGS&4)) = 0 ]; then |
| SP=$((SP-1&65535)); eval "M_$SP=$((PC>>8))" |
| SP=$((SP-1&65535)); eval "M_$SP=$((PC&255))" |
| PC=$((_Z|(_Y<<8))) |
| fi |
| ;; |
| 236) # CPE nnnn (Call if Parity Even) |
| eval "_Z=\$M_$PC"; PC=$(((PC+1)&65535)) |
| eval "_Y=\$M_$PC"; PC=$((PC+1)) |
| if [ $((FLAGS&4)) != 0 ]; then |
| SP=$((SP-1&65535)); eval "M_$SP=$((PC>>8))" |
| SP=$((SP-1&65535)); eval "M_$SP=$((PC&255))" |
| PC=$((_Z|(_Y<<8))) |
| fi |
| ;; |
| 244) # CP nnnn (Call if Plus) |
| eval "_Z=\$M_$PC"; PC=$(((PC+1)&65535)) |
| eval "_Y=\$M_$PC"; PC=$((PC+1)) |
| if [ $((FLAGS&128)) = 0 ]; then |
| SP=$((SP-1&65535)); eval "M_$SP=$((PC>>8))" |
| SP=$((SP-1&65535)); eval "M_$SP=$((PC&255))" |
| PC=$((_Z|(_Y<<8))) |
| fi |
| ;; |
| 252) # CM nnnn (Call if Minus) |
| eval "_Z=\$M_$PC"; PC=$(((PC+1)&65535)) |
| eval "_Y=\$M_$PC"; PC=$((PC+1)) |
| if [ $((FLAGS&128)) != 0 ]; then |
| SP=$((SP-1&65535)); eval "M_$SP=$((PC>>8))" |
| SP=$((SP-1&65535)); eval "M_$SP=$((PC&255))" |
| PC=$((_Z|(_Y<<8))) |
| fi |
| ;; |
| 196) # CNZ nnnn (Call if Not Zero) |
| eval "_Z=\$M_$PC"; PC=$(((PC+1)&65535)) |
| eval "_Y=\$M_$PC"; PC=$((PC+1)) |
| if [ $((FLAGS&64)) = 0 ]; then |
| SP=$((SP-1&65535)); eval "M_$SP=$((PC>>8))" |
| SP=$((SP-1&65535)); eval "M_$SP=$((PC&255))" |
| PC=$((_Z|(_Y<<8))) |
| fi |
| ;; |
| 195) # JMP nnnn |
| eval "_Z=\$M_$PC"; PC=$(((PC+1)&65535)) |
| eval "_Y=\$M_$PC"; PC=$((PC+1)) |
| PC=$((_Z|(_Y<<8))) |
| ;; |
| 194) # JNZ nnnn |
| eval "_Z=\$M_$PC"; PC=$(((PC+1)&65535)) |
| eval "_Y=\$M_$PC"; PC=$((PC+1)) |
| [ $((FLAGS&64)) = 0 ] && PC=$((_Z|(_Y<<8))) |
| ;; |
| 202) # JZ nnnn |
| eval "_Z=\$M_$PC"; PC=$(((PC+1)&65535)) |
| eval "_Y=\$M_$PC"; PC=$((PC+1)) |
| [ $((FLAGS&64)) != 0 ] && PC=$((_Z|(_Y<<8))) |
| ;; |
| 210) # JNC nnnn |
| eval "_Z=\$M_$PC"; PC=$(((PC+1)&65535)) |
| eval "_Y=\$M_$PC"; PC=$((PC+1)) |
| [ $((FLAGS&1)) = 0 ] && PC=$((_Z|(_Y<<8))) |
| ;; |
| 218) # JC nnnn |
| eval "_Z=\$M_$PC"; PC=$(((PC+1)&65535)) |
| eval "_Y=\$M_$PC"; PC=$((PC+1)) |
| [ $((FLAGS&1)) != 0 ] && PC=$((_Z|(_Y<<8))) |
| ;; |
| 226) # JPO nnnn |
| eval "_Z=\$M_$PC"; PC=$(((PC+1)&65535)) |
| eval "_Y=\$M_$PC"; PC=$((PC+1)) |
| [ $((FLAGS&4)) = 0 ] && PC=$((_Z|(_Y<<8))) |
| ;; |
| 234) # JPE nnnn |
| eval "_Z=\$M_$PC"; PC=$(((PC+1)&65535)) |
| eval "_Y=\$M_$PC"; PC=$((PC+1)) |
| [ $((FLAGS&4)) != 0 ] && PC=$((_Z|(_Y<<8))) |
| ;; |
| 242) # JP nnnn |
| eval "_Z=\$M_$PC"; PC=$(((PC+1)&65535)) |
| eval "_Y=\$M_$PC"; PC=$((PC+1)) |
| [ $((FLAGS&128)) = 0 ] && PC=$((_Z|(_Y<<8))) |
| ;; |
| 250) # JM nnnn |
| eval "_Z=\$M_$PC"; PC=$(((PC+1)&65535)) |
| eval "_Y=\$M_$PC"; PC=$((PC+1)) |
| [ $((FLAGS&128)) != 0 ] && PC=$((_Z|(_Y<<8))) |
| ;; |
| |
| 201) # RET |
| do_ret |
| ;; |
| 192) # RNZ |
| [ $((FLAGS&64)) = 0 ] && do_ret |
| ;; |
| 200) # RZ |
| [ $((FLAGS&64)) != 0 ] && do_ret |
| ;; |
| 208) # RNC |
| [ $((FLAGS&1)) = 0 ] && do_ret |
| ;; |
| 216) # RC |
| [ $((FLAGS&1)) != 0 ] && do_ret |
| ;; |
| 224) # RPO |
| [ $((FLAGS&4)) = 0 ] && do_ret |
| ;; |
| 232) # RPE |
| [ $((FLAGS&4)) != 0 ] && do_ret |
| ;; |
| 240) # RP |
| [ $((FLAGS&128)) = 0 ] && do_ret |
| ;; |
| 248) # RM |
| [ $((FLAGS&128)) != 0 ] && do_ret |
| ;; |
| |
| 205|221|237|253) # CALL nnnn |
| eval "_Z=\$M_$PC"; PC=$(((PC+1)&65535)) |
| eval "_Y=\$M_$PC"; PC=$(((PC+1)&65535)) |
| eval "M_$((SP+65535&65535))=$((PC>>8))" |
| eval "M_$((SP+65534&65535))=$((PC&255))" |
| if [ $opcode = 237 ] && [ $_Z = 237 ] && [ $_Y -gt 1 ]; then |
| eval "_Z=\$M_64508" |
| [ $_Y = 2 ] && _Y=read_disk || _Y=write_disk |
| eval "_t1=\$M_64511"; eval "_t2=\$M_64506"; eval "_t3=\$M_64507"; eval "_t4=\$M_64510"; eval "_t5=\$M_64509" |
| $_Y $((_t1-1)) $((_t2|(_t3<<8))) $((_Z|(((_t4<<8)|_t5)<<7))) && A=0 || A=1 |
| else |
| PC=$((_Z|(_Y<<8))) |
| SP=$((SP-2)) |
| fi |
| ;; |
| |
| 197) # PUSH B |
| SP=$((SP-1&65535)) |
| eval "M_$SP=\$B" |
| SP=$((SP-1&65535)) |
| eval "M_$SP=\$C" |
| ;; |
| 213) # PUSH D |
| SP=$((SP-1&65535)) |
| eval "M_$SP=\$D" |
| SP=$((SP-1&65535)) |
| eval "M_$SP=\$E" |
| ;; |
| 229) # PUSH H |
| SP=$((SP-1&65535)) |
| eval "M_$SP=\$H" |
| SP=$((SP-1&65535)) |
| eval "M_$SP=\$L" |
| ;; |
| 245) # PUSH PSW |
| SP=$((SP-1&65535)) |
| eval "M_$SP=\$A" |
| SP=$((SP-1&65535)) |
| eval "M_$SP=\$(((FLAGS&0xd7)|2))" |
| ;; |
| |
| 193) # POP B |
| eval "C=\$M_$SP" |
| SP=$(((SP+1)&65535)) |
| eval "B=\$M_$SP" |
| SP=$((SP+1)) |
| ;; |
| 209) # POP D |
| eval "E=\$M_$SP" |
| SP=$(((SP+1)&65535)) |
| eval "D=\$M_$SP" |
| SP=$((SP+1)) |
| ;; |
| 225) # POP H |
| eval "L=\$M_$SP" |
| SP=$(((SP+1)&65535)) |
| eval "H=\$M_$SP" |
| SP=$((SP+1)) |
| ;; |
| 241) # POP PSW |
| eval "FLAGS=\$((M_$SP&0xd5))" |
| SP=$(((SP+1)&65535)) |
| eval "A=\$M_$SP" |
| SP=$((SP+1)) |
| ;; |
| |
| 4) # INR B |
| B=$((B+1&255)) |
| get_pzs_flags $B |
| FLAGS=$(((FLAGS&1)|PZS|((B^(B-1))&16))) |
| ;; |
| 12) # INR C |
| C=$((C+1&255)) |
| get_pzs_flags $C |
| FLAGS=$(((FLAGS&1)|PZS|((C^(C-1))&16))) |
| ;; |
| 20) # INR D |
| D=$((D+1&255)) |
| get_pzs_flags $D |
| FLAGS=$(((FLAGS&1)|PZS|((D^(D-1))&16))) |
| ;; |
| 28) # INR E |
| E=$((E+1&255)) |
| get_pzs_flags $E |
| FLAGS=$(((FLAGS&1)|PZS|((E^(E-1))&16))) |
| ;; |
| 36) # INR H |
| H=$((H+1&255)) |
| get_pzs_flags $H |
| FLAGS=$(((FLAGS&1)|PZS|((H^(H-1))&16))) |
| ;; |
| 44) # INR L |
| L=$((L+1&255)) |
| get_pzs_flags $L |
| FLAGS=$(((FLAGS&1)|PZS|((L^(L-1))&16))) |
| ;; |
| 52) # INR M |
| _addr=$((H<<8|L)) |
| eval "_temp=\$M_$_addr" |
| _temp=$((_temp+1&255)) |
| eval "M_$_addr=\$_temp" |
| get_pzs_flags $_temp |
| FLAGS=$(((FLAGS&1)|PZS|((_temp^(_temp-1))&16))) |
| ;; |
| 60) # INR A |
| A=$((A+1&255)) |
| get_pzs_flags $A |
| FLAGS=$(((FLAGS&1)|PZS|((A^(A-1))&16))) |
| ;; |
| |
| 5) # DCR B |
| B=$((B-1&255)) |
| get_pzs_flags $B |
| FLAGS=$(((FLAGS&1)|PZS|((B^(B+1))&16))) |
| ;; |
| 13) # DCR C |
| C=$((C-1&255)) |
| get_pzs_flags $C |
| FLAGS=$(((FLAGS&1)|PZS|((C^(C+1))&16))) |
| ;; |
| 21) # DCR D |
| D=$((D-1&255)) |
| get_pzs_flags $D |
| FLAGS=$(((FLAGS&1)|PZS|((D^(D+1))&16))) |
| ;; |
| 29) # DCR E |
| E=$((E-1&255)) |
| get_pzs_flags $E |
| FLAGS=$(((FLAGS&1)|PZS|((E^(E+1))&16))) |
| ;; |
| 37) # DCR H |
| H=$((H-1&255)) |
| get_pzs_flags $H |
| FLAGS=$(((FLAGS&1)|PZS|((H^(H+1))&16))) |
| ;; |
| 45) # DCR L |
| L=$((L-1&255)) |
| get_pzs_flags $L |
| FLAGS=$(((FLAGS&1)|PZS|((L^(L+1))&16))) |
| ;; |
| 53) # DCR M |
| _addr=$((H<<8|L)) |
| eval "_temp=\$M_$_addr" |
| _temp=$((_temp-1&255)) |
| eval "M_$_addr=\$_temp" |
| get_pzs_flags $_temp |
| FLAGS=$(((FLAGS&1)|PZS|((_temp^(_temp+1))&16))) |
| ;; |
| 61) # DCR A |
| A=$((A-1&255)) |
| get_pzs_flags $A |
| FLAGS=$(((FLAGS&1)|PZS|((A^(A+1))&16))) |
| ;; |
| |
| # INX/DCX instructions (16-bit increment/decrement) |
| 3) # INX B |
| C=$((C+1&255)) |
| [ $C = 0 ] && B=$((B+1&255)) |
| ;; |
| 19) # INX D |
| E=$((E+1&255)) |
| [ $E = 0 ] && D=$((D+1&255)) |
| ;; |
| 35) # INX H |
| L=$((L+1&255)) |
| [ $L = 0 ] && H=$((H+1&255)) |
| ;; |
| 51) # INX SP |
| SP=$((SP+1&65535)) |
| ;; |
| 11) # DCX B |
| C=$((C-1&255)) |
| [ $C = 255 ] && B=$((B-1&255)) |
| ;; |
| 27) # DCX D |
| E=$((E-1&255)) |
| [ $E = 255 ] && D=$((D-1&255)) |
| ;; |
| 43) # DCX H |
| L=$((L-1&255)) |
| [ $L = 255 ] && H=$((H-1&255)) |
| ;; |
| 59) # DCX SP |
| SP=$((SP-1&65535)) |
| ;; |
| |
| 2) # STAX B |
| eval "M_$((B<<8|C))=\$A" |
| ;; |
| 18) # STAX D |
| eval "M_$((D<<8|E))=\$A" |
| ;; |
| 10) # LDAX B |
| eval "A=\$M_$((B<<8|C))" |
| ;; |
| 26) # LDAX D |
| eval "A=\$M_$((D<<8|E))" |
| ;; |
| 42) # LHLD nnnn |
| eval "_Z=\$M_$PC"; PC=$((PC+1)) |
| eval "_Y=\$M_$PC"; PC=$((PC+1)) |
| _addr=$((_Z|(_Y<<8))) |
| eval "L=\$M_$_addr" |
| eval "H=\$M_$((_addr+1))" |
| ;; |
| 34) # SHLD nnnn |
| eval "_Z=\$M_$PC"; PC=$((PC+1)) |
| eval "_Y=\$M_$PC"; PC=$((PC+1)) |
| _addr=$((_Z|(_Y<<8))) |
| eval "M_$_addr=\$L" |
| eval "M_$((_addr+1))=\$H" |
| ;; |
| 58) # LDA nnnn |
| eval "_Z=\$M_$PC"; PC=$((PC+1)) |
| eval "_Y=\$M_$PC"; PC=$((PC+1)) |
| eval "A=\$M_$((_Z|(_Y<<8)))" |
| ;; |
| 50) # STA nnnn |
| eval "_Z=\$M_$PC"; PC=$((PC+1)) |
| eval "_Y=\$M_$PC"; PC=$((PC+1)) |
| eval "M_$((_Z|(_Y<<8)))=\$A" |
| ;; |
| 9) # DAD B |
| _HL=$((H<<8|L)) |
| _BC=$((B<<8|C)) |
| _R=$((_HL+_BC)) |
| H=$((_R>>8&255)) |
| L=$((_R&255)) |
| FLAGS=$(((FLAGS&0xfe)|(_R>>16&1))) |
| ;; |
| 25) # DAD D |
| _HL=$((H<<8|L)) |
| _DE=$((D<<8|E)) |
| _R=$((_HL+_DE)) |
| H=$((_R>>8&255)) |
| L=$((_R&255)) |
| FLAGS=$(((FLAGS&0xfe)|(_R>>16&1))) |
| ;; |
| 41) # DAD H |
| _HL=$((H<<8|L)) |
| _R=$((_HL+_HL)) |
| H=$((_R>>8&255)) |
| L=$((_R&255)) |
| FLAGS=$(((FLAGS&0xfe)|(_R>>16&1))) |
| ;; |
| 57) # DAD SP |
| _HL=$((H<<8|L)) |
| _R=$((_HL+SP)) |
| H=$((_R>>8&255)) |
| L=$((_R&255)) |
| FLAGS=$(((FLAGS&0xfe)|(_R>>16&1))) |
| ;; |
| 7) # RLC |
| _bit=$((A>>7)) |
| A=$(((A<<1|_bit)&255)) |
| FLAGS=$(((FLAGS&0xfe)|_bit)) |
| ;; |
| 15) # RRC |
| _bit=$((A&1)) |
| A=$(((A>>1)|(_bit<<7))) |
| FLAGS=$(((FLAGS&0xfe)|_bit)) |
| ;; |
| 23) # RAL |
| _temp=$A |
| A=$(((A<<1|(FLAGS&1))&255)) |
| FLAGS=$(((FLAGS&0xfe)|(_temp>>7))) |
| ;; |
| 31) # RAR |
| _temp=$A |
| A=$((A>>1|(FLAGS&1)<<7)) |
| FLAGS=$(((FLAGS&0xfe)|(_temp&1))) |
| ;; |
| 39) # DAA |
| _carry=$((FLAGS&1)) |
| [ $((A&15)) -gt 9 ] || [ $((FLAGS&16)) != 0 ] && A=$((A+6)) |
| [ $A -gt 159 ] || [ $_carry != 0 ] && A=$((A+96)) && _carry=1 |
| A=$((A&255)) |
| get_pzs_flags $A |
| FLAGS=$(((_carry)|PZS|((A^(A-1))>>4&16))) |
| ;; |
| 47) # CMA |
| A=$((A^255)) |
| ;; |
| 55) # STC |
| FLAGS=$((FLAGS|1)) |
| ;; |
| 63) # CMC |
| FLAGS=$((FLAGS^1)) |
| ;; |
| 233) # PCHL |
| PC=$((H<<8|L)) |
| ;; |
| 249) # SPHL |
| SP=$((H<<8|L)) |
| ;; |
| 235) # XCHG |
| _temp=$D |
| D=$H |
| H=$_temp |
| _temp=$E |
| E=$L |
| L=$_temp |
| ;; |
| 227) # XTHL |
| _temp=$L |
| eval "L=\$M_$SP" |
| eval "M_$SP=\$_temp" |
| _temp=$H |
| eval "H=\$M_$(((SP+1)&65535))" |
| eval "M_$(((SP+1)&65535))=\$_temp" |
| ;; |
| |
| 243) # DI |
| ;; |
| 251) # EI |
| ;; |
| |
| 211) # OUT nn |
| eval "port=\$M_$PC" |
| case $port in |
| 0|2) |
| printf "\\`printf %03o $A`" |
| ;; |
| esac |
| PC=$((PC+1)) |
| ;; |
| 219) # IN nn |
| eval "port=\$M_$PC" |
| case $port in |
| 0) |
| if [ -n "$key" ] || read_key --check; then |
| A=255 |
| else |
| A=0 |
| fi |
| ;; |
| 1) |
| if [ -n "$key" ] || read_key; then |
| A=$key |
| key= |
| else |
| A=0 |
| fi |
| ;; |
| 2) |
| A=0 |
| ;; |
| esac |
| PC=$((PC+1)) |
| ;; |
| |
| 199) # RST 0 |
| SP=$((SP-1&65535)); eval "M_$SP=$((PC>>8))" |
| SP=$((SP-1&65535)); eval "M_$SP=$((PC&255))" |
| PC=0 |
| ;; |
| 207) # RST 1 |
| SP=$((SP-1&65535)); eval "M_$SP=$((PC>>8))" |
| SP=$((SP-1&65535)); eval "M_$SP=$((PC&255))" |
| PC=8 |
| ;; |
| 215) # RST 2 |
| SP=$((SP-1&65535)); eval "M_$SP=$((PC>>8))" |
| SP=$((SP-1&65535)); eval "M_$SP=$((PC&255))" |
| PC=16 |
| ;; |
| 223) # RST 3 |
| SP=$((SP-1&65535)); eval "M_$SP=$((PC>>8))" |
| SP=$((SP-1&65535)); eval "M_$SP=$((PC&255))" |
| PC=24 |
| ;; |
| 231) # RST 4 |
| SP=$((SP-1&65535)); eval "M_$SP=$((PC>>8))" |
| SP=$((SP-1&65535)); eval "M_$SP=$((PC&255))" |
| PC=32 |
| ;; |
| 239) # RST 5 |
| SP=$((SP-1&65535)); eval "M_$SP=$((PC>>8))" |
| SP=$((SP-1&65535)); eval "M_$SP=$((PC&255))" |
| PC=40 |
| ;; |
| 247) # RST 6 |
| SP=$((SP-1&65535)); eval "M_$SP=$((PC>>8))" |
| SP=$((SP-1&65535)); eval "M_$SP=$((PC&255))" |
| PC=48 |
| ;; |
| 255) # RST 7 |
| SP=$((SP-1&65535)); eval "M_$SP=$((PC>>8))" |
| SP=$((SP-1&65535)); eval "M_$SP=$((PC&255))" |
| PC=56 |
| ;; |
| |
| *) |
| printf "Unimplemented opcode 0x%02x\\n" $opcode 1>&2 |
| ;; |
| esac |
| PC=$((PC&65535)) |
| SP=$((SP&65535)) |
| #printf '\r \rPC=%04x SP=%04x\r' $PC $SP 1>&2 |
| done |