mirror of
https://github.com/phabrics/Run-Sun3-SunOS-4.1.1.git
synced 2026-04-29 11:02:59 -04:00
2151 lines
79 KiB
Bash
2151 lines
79 KiB
Bash
#! /bin/sh
|
|
|
|
# $Id: m68k-insns-auto.sh,v 1.26 2009/08/29 19:38:23 fredette Exp $
|
|
|
|
# ic/m68k/m68k-insns-auto.sh - automatically generates C code
|
|
# for many m68k emulation instructions:
|
|
|
|
#
|
|
# Copyright (c) 2002, 2003 Matt Fredette
|
|
# All rights reserved.
|
|
#
|
|
# Redistribution and use in source and binary forms, with or without
|
|
# modification, are permitted provided that the following conditions
|
|
# are met:
|
|
# 1. Redistributions of source code must retain the above copyright
|
|
# notice, this list of conditions and the following disclaimer.
|
|
# 2. Redistributions in binary form must reproduce the above copyright
|
|
# notice, this list of conditions and the following disclaimer in the
|
|
# documentation and/or other materials provided with the distribution.
|
|
# 3. All advertising materials mentioning features or use of this software
|
|
# must display the following acknowledgement:
|
|
# This product includes software developed by Matt Fredette.
|
|
# 4. The name of the author may not be used to endorse or promote products
|
|
# derived from this software without specific prior written permission.
|
|
#
|
|
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
|
# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
# DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
|
|
# INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
|
# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
|
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
# POSSIBILITY OF SUCH DAMAGE.
|
|
#
|
|
|
|
header=false
|
|
|
|
for option
|
|
do
|
|
case $option in
|
|
--header) header=true ;;
|
|
esac
|
|
done
|
|
|
|
PROG=`basename $0`
|
|
cat <<EOF
|
|
/* automatically generated by $PROG, do not edit! */
|
|
_TME_RCSID("\$Id: m68k-insns-auto.sh,v 1.26 2009/08/29 19:38:23 fredette Exp $");
|
|
|
|
EOF
|
|
if $header; then :; else
|
|
cat <<EOF
|
|
#include "m68k-impl.h"
|
|
|
|
EOF
|
|
fi
|
|
|
|
# permute for the three different operand sizes we need to handle:
|
|
for size in 8 16 32; do
|
|
|
|
# the shifts needed to get register contents of a specific size:
|
|
case ${size} in
|
|
8) reg_size_shift=' << 2' ;;
|
|
16) reg_size_shift=' << 1' ;;
|
|
32) reg_size_shift='' ;;
|
|
esac
|
|
|
|
# generate the ALU functions:
|
|
for name in add sub cmp neg or and eor not tst move moveq clr cmpa negx addx subx cmpm; do
|
|
|
|
# characterize each operation:
|
|
optype=normal ; src=op0 ; dst=op1 ; res=op1 ; arith=no ; with_x=false ; store_res=true
|
|
case "$name" in
|
|
add) op=' + ' ; arith=add ;;
|
|
sub) op=' - ' ; arith=sub ;;
|
|
cmp) op=' - ' ; arith=sub ; store_res=false ;;
|
|
neg) op=' - ' ; arith=sub ; dst=0 ; src=op1 ;;
|
|
or) op=' | ' ;;
|
|
and) op=' & ' ;;
|
|
eor) op=' ^ ' ;;
|
|
not) op='~ ' ; dst= ; src=op1 ;;
|
|
tst) op='' ; dst= ; src=op1 ; store_res=false ;;
|
|
move) op='' ; dst= ; src=op1 ; res=op0 ;;
|
|
moveq) op='' ; dst= ; src=opc8 ; if test ${size} != 32; then continue; fi ;;
|
|
clr) op='' ; dst= ; src=0 ;;
|
|
cmpa) op=' - ' ; arith=sub ; src=op0.16s32 ; store_res=false
|
|
if test $size != 16; then continue; fi ;;
|
|
negx) op=' - ' ; arith=sub ; with_x=true ; dst=0 ; src=op1 ;;
|
|
addx) op=' + ' ; arith=add ; optype=mathx ; with_x=true ;;
|
|
subx) op=' - ' ; arith=sub ; optype=mathx ; with_x=true ;;
|
|
cmpm) op=' - ' ; arith=sub ; optype=mathx ; store_res=false ;;
|
|
*) echo "$0 internal error: unknown ALU function $name" 1>&2 ; exit 1 ;;
|
|
esac
|
|
|
|
# placeholder for another permutation:
|
|
:
|
|
|
|
# if we're making the header, just emit a declaration:
|
|
if $header; then
|
|
echo "TME_M68K_INSN_DECL(tme_m68k_${name}${size});"
|
|
continue
|
|
fi
|
|
|
|
# open the function:
|
|
echo ""
|
|
echo -n "/* this does a ${size}-bit \"$name "
|
|
case "${src}/${dst}" in *op0*) echo -n "SRC, " ;; esac
|
|
echo "DST\": */"
|
|
echo "TME_M68K_INSN(tme_m68k_${name}${size})"
|
|
echo "{"
|
|
|
|
# declare our locals:
|
|
if test $name = cmpa; then size=32; fi
|
|
echo -n " tme_uint${size}_t res"
|
|
case "${src}/${dst}" in *op0*) echo -n ", op0" ;; esac
|
|
case "${src}/${dst}" in *op1*) echo -n ", op1" ;; esac
|
|
echo ";"
|
|
echo " tme_uint8_t flags;"
|
|
|
|
# load the operand(s):
|
|
echo ""
|
|
echo " /* load the operand(s): */"
|
|
case ${optype} in
|
|
mathx)
|
|
echo " unsigned int function_code = TME_M68K_FUNCTION_CODE_DATA(ic);"
|
|
|
|
# NB: in my 68000 Programmer's Manual, the description
|
|
# of subx is a little backwards from addx and cmpm. in
|
|
# subx, the reg field at bits 0-2 is called the "x"
|
|
# field, where in addx and cmpm it's called the "y"
|
|
# field, and similarly for the reg field at bits 9-11.
|
|
# fortunately, the meanings of the two reg fields is
|
|
# always the same despite this - the reg field at bits
|
|
# 0-2 always identifies the source operand, and the
|
|
# reg field at bits 9-11 always identifies the
|
|
# destination operand:
|
|
echo " int ireg_src = TME_FIELD_EXTRACTU(TME_M68K_INSN_OPCODE, 0, 3);"
|
|
echo " int ireg_dst = TME_FIELD_EXTRACTU(TME_M68K_INSN_OPCODE, 9, 3);"
|
|
|
|
# the stack pointer must always be adjusted by a multiple of two.
|
|
# assuming ireg < 8, ((ireg + 1) >> 3) == 1 iff ireg == 7, meaning %a7:
|
|
echo -n " tme_uint32_t ireg_src_adjust = sizeof(tme_uint${size}_t)";
|
|
if test ${size} = 8; then
|
|
echo -n " + ((ireg_src + 1) >> 3)"
|
|
fi
|
|
echo ";"
|
|
echo -n " tme_uint32_t ireg_dst_adjust = sizeof(tme_uint${size}_t)";
|
|
if test ${size} = 8; then
|
|
echo -n " + ((ireg_dst + 1) >> 3)"
|
|
fi
|
|
echo ";"
|
|
|
|
case ${name} in
|
|
|
|
# cmpm always uses memory and is always postincrement:
|
|
cmpm)
|
|
echo ""
|
|
echo " TME_M68K_INSN_CANFAULT;"
|
|
echo ""
|
|
echo " if (!TME_M68K_SEQUENCE_RESTARTING) {"
|
|
echo " ic->_tme_m68k_ea_function_code = function_code;"
|
|
echo " ic->_tme_m68k_ea_address = ic->tme_m68k_ireg_uint32(TME_M68K_IREG_A0 + ireg_src);"
|
|
echo " ic->tme_m68k_ireg_uint32(TME_M68K_IREG_A0 + ireg_src) += ireg_src_adjust;"
|
|
echo " }"
|
|
echo " tme_m68k_read_mem${size}(ic, TME_M68K_IREG_MEMY${size});"
|
|
echo " if (!TME_M68K_SEQUENCE_RESTARTING) {"
|
|
echo " ic->_tme_m68k_ea_function_code = function_code;"
|
|
echo " ic->_tme_m68k_ea_address = ic->tme_m68k_ireg_uint32(TME_M68K_IREG_A0 + ireg_dst);"
|
|
echo " ic->tme_m68k_ireg_uint32(TME_M68K_IREG_A0 + ireg_dst) += ireg_dst_adjust;"
|
|
echo " }"
|
|
echo " tme_m68k_read_memx${size}(ic);"
|
|
echo " ${dst} = ic->tme_m68k_ireg_memx${size};"
|
|
echo " ${src} = ic->tme_m68k_ireg_memy${size};"
|
|
;;
|
|
|
|
# addx and subx use either registers or memory. if they use memory,
|
|
# they always predecrement:
|
|
addx|subx)
|
|
echo " tme_uint16_t memory;"
|
|
echo ""
|
|
echo " memory = (TME_M68K_INSN_OPCODE & TME_BIT(3));"
|
|
echo " if (memory) {"
|
|
echo " TME_M68K_INSN_CANFAULT;"
|
|
echo " if (!TME_M68K_SEQUENCE_RESTARTING) {"
|
|
echo " ic->tme_m68k_ireg_uint32(TME_M68K_IREG_A0 + ireg_src) -= ireg_src_adjust;"
|
|
echo " ic->_tme_m68k_ea_function_code = function_code;"
|
|
echo " ic->_tme_m68k_ea_address = ic->tme_m68k_ireg_uint32(TME_M68K_IREG_A0 + ireg_src);"
|
|
echo " }"
|
|
echo " tme_m68k_read_mem${size}(ic, TME_M68K_IREG_MEMY${size});"
|
|
echo " if (!TME_M68K_SEQUENCE_RESTARTING) {"
|
|
echo " ic->tme_m68k_ireg_uint32(TME_M68K_IREG_A0 + ireg_dst) -= ireg_dst_adjust;"
|
|
echo " ic->_tme_m68k_ea_function_code = function_code;"
|
|
echo " ic->_tme_m68k_ea_address = ic->tme_m68k_ireg_uint32(TME_M68K_IREG_A0 + ireg_dst);"
|
|
echo " }"
|
|
echo " tme_m68k_read_memx${size}(ic);"
|
|
echo " ${dst} = ic->tme_m68k_ireg_memx${size};"
|
|
echo " ${src} = ic->tme_m68k_ireg_memy${size};"
|
|
echo " }"
|
|
echo " else {"
|
|
echo " ${src} = ic->tme_m68k_ireg_uint${size}((TME_M68K_IREG_D0 + ireg_src)${reg_size_shift});"
|
|
echo " ${dst} = ic->tme_m68k_ireg_uint${size}((TME_M68K_IREG_D0 + ireg_dst)${reg_size_shift});"
|
|
echo " }"
|
|
;;
|
|
*) echo "$0 internal error: unknown mathx ${name}" 1>&2 ; exit 1 ;;
|
|
esac
|
|
;;
|
|
normal)
|
|
for which in src dst; do
|
|
eval 'what=$'${which}
|
|
case "x${what}" in
|
|
x|x0) ;;
|
|
xop[01].16s32)
|
|
what=`echo ${what} | sed -e 's/\..*//'`
|
|
eval ${which}"=${what}"
|
|
echo " ${what} = (tme_uint32_t) ((tme_int32_t) *((tme_int16_t *) _${what}));"
|
|
;;
|
|
xop[01])
|
|
echo " ${what} = *((tme_uint${size}_t *) _${what});"
|
|
;;
|
|
xopc8)
|
|
eval ${which}"='TME_EXT_S8_U${size}((tme_int8_t) (TME_M68K_INSN_OPCODE & 0xff))'"
|
|
;;
|
|
*) echo "$0 internal error: unknown what ${what}" 1>&2 ; exit 1 ;;
|
|
esac
|
|
done
|
|
;;
|
|
*) echo "$0 internal error: unknown optype ${optype}" 1>&2 ; exit 1 ;;
|
|
esac
|
|
|
|
# perform the operation:
|
|
echo ""
|
|
echo " /* perform the operation: */"
|
|
echo -n " res = ${dst}${op}${src}"
|
|
if $with_x; then
|
|
echo -n "${op}((ic->tme_m68k_ireg_ccr / TME_M68K_FLAG_X) & 1)"
|
|
fi
|
|
echo ";"
|
|
|
|
# store the result:
|
|
if $store_res; then
|
|
echo ""
|
|
echo " /* store the result: */"
|
|
case ${optype} in
|
|
mathx)
|
|
echo " if (memory) {"
|
|
echo " if (!TME_M68K_SEQUENCE_RESTARTING) {"
|
|
echo " ic->tme_m68k_ireg_memx${size} = res;"
|
|
echo " ic->_tme_m68k_ea_function_code = function_code;"
|
|
echo " ic->_tme_m68k_ea_address = ic->tme_m68k_ireg_uint32(TME_M68K_IREG_A0 + ireg_dst);"
|
|
echo " }"
|
|
echo " tme_m68k_write_memx${size}(ic);"
|
|
echo " }"
|
|
echo " else {"
|
|
echo " ic->tme_m68k_ireg_uint${size}((TME_M68K_IREG_D0 + ireg_dst)${reg_size_shift}) = res;"
|
|
echo " }"
|
|
;;
|
|
normal)
|
|
echo " *((tme_uint${size}_t *) _${res}) = res;"
|
|
;;
|
|
*) echo "$0 internal error: unknown optype ${optype}" 1>&2 ; exit 1 ;;
|
|
esac
|
|
fi
|
|
|
|
# start the status flags, maybe preserving X:
|
|
echo ""
|
|
echo " /* set the flags: */"
|
|
case "${name}:${arith}" in
|
|
cmp*|*:no)
|
|
flag_x=
|
|
;;
|
|
*)
|
|
flag_x=" | TME_M68K_FLAG_X"
|
|
;;
|
|
esac
|
|
|
|
# set N. we cast to tme_uint8_t as soon as we know the
|
|
# bit we want is within the range of the type, to try
|
|
# to affect the generated assembly:
|
|
echo " flags = ((tme_uint8_t) (((tme_uint${size}_t) res) >> (${size} - 1))) * TME_M68K_FLAG_N;"
|
|
|
|
# set Z:
|
|
if $with_x; then
|
|
echo " if (res == 0) flags |= (ic->tme_m68k_ireg_ccr & TME_M68K_FLAG_Z);"
|
|
else
|
|
echo " if (res == 0) flags |= TME_M68K_FLAG_Z;"
|
|
fi
|
|
|
|
# set V and C and maybe X:
|
|
case $arith in
|
|
add)
|
|
case $size in
|
|
8) ones="0xff" ;;
|
|
16) ones="0xffff" ;;
|
|
32) ones="0xffffffff" ;;
|
|
esac
|
|
# if the operands are the same sign, and the result has
|
|
# a different sign, set V. we cast to tme_uint8_t as
|
|
# soon as we know the bit we want is within the range
|
|
# of the type, to try to affect the generated assembly:
|
|
echo " flags |= ((tme_uint8_t) (((${src} ^ ${dst} ^ ${ones}) & (${dst} ^ res)) >> (${size} - 1))) * TME_M68K_FLAG_V;"
|
|
# if src is greater than the logical inverse of dst, set C:
|
|
echo -n " if (${src} > (${dst} ^ ${ones})"
|
|
if $with_x; then
|
|
echo -n " || (${src} == (${dst} ^ ${ones}) && (ic->tme_m68k_ireg_ccr & TME_M68K_FLAG_X))"
|
|
fi
|
|
echo ") flags |= TME_M68K_FLAG_C${flag_x};"
|
|
;;
|
|
sub)
|
|
# if the operands are different signs, and the result has
|
|
# a different sign from the first operand, set V. we
|
|
# cast to tme_uint8_t as soon as we know the bit we want
|
|
# is within the range of the type, to try to affect the
|
|
# generated assembly:
|
|
echo " flags |= ((tme_uint8_t) (((${src} ^ ${dst}) & (${dst} ^ res)) >> (${size} - 1))) * TME_M68K_FLAG_V;"
|
|
# if src is greater than dst, set C:
|
|
echo -n " if (${src} > ${dst}"
|
|
if $with_x; then
|
|
echo -n " || (${src} == ${dst} && (ic->tme_m68k_ireg_ccr & TME_M68K_FLAG_X))"
|
|
fi
|
|
echo ") flags |= TME_M68K_FLAG_C${flag_x};"
|
|
;;
|
|
no) ;;
|
|
esac
|
|
|
|
# preserve X:
|
|
if test "x${flag_x}" = x; then
|
|
echo " flags |= (ic->tme_m68k_ireg_ccr & TME_M68K_FLAG_X);"
|
|
fi
|
|
|
|
# set the flags:
|
|
echo " ic->tme_m68k_ireg_ccr = flags;"
|
|
|
|
# done:
|
|
echo ""
|
|
echo " TME_M68K_INSN_OK;"
|
|
echo "}"
|
|
|
|
if test $name = cmpa; then size=16; fi
|
|
done
|
|
|
|
# generate the wrapper functions for a move of an address register
|
|
# to a predecrement or postincrement EA with that same address
|
|
# register:
|
|
for name in pd pi; do
|
|
|
|
# a move of an address registers are only word and long:
|
|
if test $size = 8; then continue; fi
|
|
|
|
# if we're making the header, just emit a declaration:
|
|
if $header; then
|
|
echo "TME_M68K_INSN_DECL(tme_m68k_move_sr${name}${size});"
|
|
continue
|
|
fi
|
|
|
|
echo ""
|
|
echo "/* a move of an address register to a predecrement or"
|
|
echo " postincrement EA with that same address register, must"
|
|
echo " store the original value of the address register. since the"
|
|
echo " predecrement and postincrement code in the executer updates"
|
|
echo " the address register before the move has happened, we wrap"
|
|
echo " the normal move function in this one, that gives an op1"
|
|
echo " argument that is the original value of the address register: */"
|
|
echo "TME_M68K_INSN(tme_m68k_move_sr${name}${size})"
|
|
echo "{"
|
|
if test ${name} = pd; then op='+'; else op='-'; fi
|
|
echo " /* NB: both this function and tme_m68k_move${size}()"
|
|
echo " get the source operand as _op1, and the destination"
|
|
echo " operand as _op0: */"
|
|
echo " if (!TME_M68K_SEQUENCE_RESTARTING) {"
|
|
echo " *((tme_uint${size}_t *) _op0)"
|
|
echo " = (*((tme_uint${size}_t *) _op1)"
|
|
echo " ${op} sizeof(tme_uint${size}_t));"
|
|
echo " }"
|
|
echo " tme_m68k_move${size}(ic, _op0, _op0);"
|
|
echo "}"
|
|
done
|
|
|
|
# generate the address math functions:
|
|
for name in suba adda movea; do
|
|
|
|
# the address math functions don't need an 8-bit version:
|
|
if test $size = 8; then continue; fi
|
|
|
|
# if we're making the header, just emit a declaration:
|
|
if $header; then
|
|
echo "TME_M68K_INSN_DECL(tme_m68k_${name}${size});"
|
|
continue
|
|
fi
|
|
|
|
echo ""
|
|
echo "/* the ${name} function on a ${size}-byte EA: */"
|
|
echo "TME_M68K_INSN(tme_m68k_${name}${size})"
|
|
echo "{"
|
|
case $name in
|
|
suba) op='-' ; src="_op0" ; dst="_op1" ;;
|
|
adda) op='+' ; src="_op0" ; dst="_op1" ;;
|
|
movea) op='' ; src="_op1" ; dst="_op0" ;;
|
|
esac
|
|
echo " *((tme_int32_t *) ${dst}) ${op}= *((tme_int${size}_t *) ${src});"
|
|
echo " TME_M68K_INSN_OK;"
|
|
echo "}"
|
|
done
|
|
|
|
# generate the bit functions:
|
|
for name in btst bchg bclr bset; do
|
|
|
|
# the bit functions don't need a 16-bit version:
|
|
if test $size = 16; then continue; fi
|
|
|
|
# if we're making the header, just emit a declaration:
|
|
if $header; then
|
|
echo "TME_M68K_INSN_DECL(tme_m68k_${name}${size});"
|
|
continue
|
|
fi
|
|
|
|
echo ""
|
|
echo "/* the ${name} function on a ${size}-byte EA: */"
|
|
echo "TME_M68K_INSN(tme_m68k_${name}${size})"
|
|
echo "{"
|
|
echo " tme_uint${size}_t value, bit;"
|
|
echo " bit = _TME_BIT(tme_uint${size}_t, TME_M68K_INSN_OP0(tme_uint8_t) & (${size} - 1));"
|
|
echo " value = TME_M68K_INSN_OP1(tme_uint${size}_t);"
|
|
echo " if (value & bit) {"
|
|
echo " ic->tme_m68k_ireg_ccr &= ~TME_M68K_FLAG_Z;"
|
|
echo " }"
|
|
echo " else {"
|
|
echo " ic->tme_m68k_ireg_ccr |= TME_M68K_FLAG_Z;"
|
|
echo " }"
|
|
case ${name} in
|
|
btst) ;;
|
|
bchg) echo " TME_M68K_INSN_OP1(tme_uint${size}_t) = value ^ bit;" ;;
|
|
bclr) echo " TME_M68K_INSN_OP1(tme_uint${size}_t) = value & ~bit;" ;;
|
|
bset) echo " TME_M68K_INSN_OP1(tme_uint${size}_t) = value | bit;" ;;
|
|
esac
|
|
echo " TME_M68K_INSN_OK;"
|
|
echo "}"
|
|
done
|
|
|
|
# generate the shift/rotate functions:
|
|
for func in as ls ro rox; do
|
|
for dir in l r; do
|
|
name="${func}${dir}"
|
|
|
|
# if we're making the header, just emit a declaration:
|
|
if $header; then
|
|
echo "TME_M68K_INSN_DECL(tme_m68k_${name}${size});"
|
|
continue
|
|
fi
|
|
|
|
echo ""
|
|
echo "/* the ${name} function on a ${size}-byte EA: */"
|
|
echo "TME_M68K_INSN(tme_m68k_${name}${size})"
|
|
echo "{"
|
|
echo " unsigned int count;"
|
|
sign=u
|
|
case "${name}" in
|
|
asr) sign= ;;
|
|
asl) echo " tme_uint${size}_t sign_bits, sign_bits_mask;" ;;
|
|
rox[lr]) echo " tme_uint8_t xbit;" ;;
|
|
*) ;;
|
|
esac
|
|
echo " tme_${sign}int${size}_t res;"
|
|
echo " tme_uint8_t flags;"
|
|
echo ""
|
|
echo " /* get the count and operand: */"
|
|
echo " count = TME_M68K_INSN_OP0(tme_uint8_t) & 63;"
|
|
echo " res = TME_M68K_INSN_OP1(tme_${sign}int${size}_t);"
|
|
|
|
echo ""
|
|
echo " /* generate the X, V, and C flags assuming the count is zero: */"
|
|
echo " flags = ic->tme_m68k_ireg_ccr & TME_M68K_FLAG_X;"
|
|
case "${name}" in
|
|
rox[lr])
|
|
echo " xbit = (flags / TME_M68K_FLAG_X);"
|
|
echo " flags |= (xbit * TME_M68K_FLAG_C);"
|
|
;;
|
|
esac
|
|
|
|
echo ""
|
|
echo " /* if the count is nonzero, update the result and"
|
|
echo " generate the X, V, and C flags: */"
|
|
echo " if (count > 0) {"
|
|
case "${name}" in
|
|
lsr)
|
|
echo " if (63 > SHIFTMAX_INT${size}_T"
|
|
echo " && count > ${size}) {"
|
|
echo " res = 0;"
|
|
echo " }"
|
|
echo " res >>= (count - 1);"
|
|
echo " flags = (res & 1);"
|
|
echo " flags *= TME_M68K_FLAG_C;"
|
|
echo " flags |= (flags * TME_M68K_FLAG_X);"
|
|
echo " res >>= 1;"
|
|
;;
|
|
asr)
|
|
echo " if (63 > SHIFTMAX_INT${size}_T"
|
|
echo " && count > ${size}) {"
|
|
echo " res = 0 - (res < 0);"
|
|
echo " }"
|
|
echo "#ifdef SHIFTSIGNED_INT${size}_T"
|
|
echo " res >>= (count - 1);"
|
|
echo "#else /* !SHIFTSIGNED_INT${size}_T */"
|
|
echo " for (; --count > 0; ) {"
|
|
echo " res = (res & ~((tme_${sign}int${size}_t) 1)) / 2;"
|
|
echo " }"
|
|
echo "#endif /* !SHIFTSIGNED_INT${size}_T */"
|
|
echo " flags = (res & 1);"
|
|
echo " flags *= TME_M68K_FLAG_C;"
|
|
echo " flags |= (flags * TME_M68K_FLAG_X);"
|
|
echo "#ifdef SHIFTSIGNED_INT${size}_T"
|
|
echo " res >>= 1;"
|
|
echo "#else /* !SHIFTSIGNED_INT${size}_T */"
|
|
echo " res = (res & ~((tme_${sign}int${size}_t) 1)) / 2;"
|
|
echo "#endif /* !SHIFTSIGNED_INT${size}_T */"
|
|
;;
|
|
[al]sl)
|
|
if test ${name} = asl; then
|
|
echo ""
|
|
echo " /* we need to see how the sign of the result will change during"
|
|
echo " shifting in order to generate V."
|
|
echo ""
|
|
echo " in general, the idea is to get all of the bits that will ever"
|
|
echo " appear in the sign position into sign_bits, with a mask in"
|
|
echo " sign_bits_mask. if (sign_bits & sign_bits_mask) is zero or"
|
|
echo " sign_bits_mask, clear V, else set V."
|
|
echo ""
|
|
echo " start by loading the operand into sign_bits and setting"
|
|
echo " sign_bits_mask to all-bits-one."
|
|
echo ""
|
|
echo " if the shift count is exactly ${size} - 1, then all of the bits"
|
|
echo " of the operand will appear in the sign position."
|
|
echo ""
|
|
echo " if the shift count is less than ${size} - 1, then some of the"
|
|
echo " less significant bits of the operand will never appear in the"
|
|
echo " sign position, so we can shift sign_bits_mask to ignore them."
|
|
echo ""
|
|
echo " if the shift count is greater than ${size} - 1, then all of the"
|
|
echo " bits in the operand, plus at least one zero bit, will appear in"
|
|
echo " the sign position. the only way that the sign bit will never"
|
|
echo " change during the shift is if the operand was zero to begin with."
|
|
echo " without any changes to sign_bits or sign_bits_mask, the final"
|
|
echo " test will always work, except when sign_bits is all-bits-one."
|
|
echo " the magic below clears the least-significant bit of sign_bits"
|
|
echo " iff sign_bits is all-bits-one: */"
|
|
echo " sign_bits = res;"
|
|
fi
|
|
echo " if (63 > SHIFTMAX_INT${size}_T"
|
|
echo " && count > ${size}) {"
|
|
echo " res = 0;"
|
|
echo " }"
|
|
echo " res <<= (count - 1);"
|
|
echo " flags = (res >> (${size} - 1));"
|
|
echo " flags *= TME_M68K_FLAG_C;"
|
|
echo " flags |= (flags * TME_M68K_FLAG_X);"
|
|
echo " res <<= 1;"
|
|
if test ${name} = asl; then
|
|
echo " sign_bits_mask = (tme_uint${size}_t) -1;"
|
|
echo " if (count != ${size} - 1) {"
|
|
echo " if (count < ${size}) {"
|
|
echo " sign_bits_mask <<= ((${size} - 1) - count);"
|
|
echo " }"
|
|
echo " else {"
|
|
echo " sign_bits ^= !(sign_bits + 1);"
|
|
echo " }"
|
|
echo " }"
|
|
echo " sign_bits &= sign_bits_mask;"
|
|
echo " if (sign_bits != 0 && sign_bits != sign_bits_mask) {"
|
|
echo " flags |= TME_M68K_FLAG_V;"
|
|
echo " }"
|
|
fi
|
|
;;
|
|
ro[lr])
|
|
echo " count &= (${size} - 1);"
|
|
if test $dir = l; then
|
|
echo " res = (res << count) | (res >> (${size} - count));"
|
|
echo " flags |= ((res & 1) * TME_M68K_FLAG_C);"
|
|
else
|
|
echo " res = (res << (${size} - count)) | (res >> count);"
|
|
echo " flags |= ((res >> (${size} - 1)) * TME_M68K_FLAG_C);"
|
|
fi
|
|
;;
|
|
rox[lr])
|
|
echo " count %= (${size} + 1);"
|
|
echo " flags = xbit;"
|
|
echo " if (count > 0) {"
|
|
if test $dir = l; then
|
|
echo " flags = (res >> (${size} - count)) & 1;"
|
|
echo " if (${size} > SHIFTMAX_INT${size}_T"
|
|
echo " && count == ${size}) {"
|
|
echo " res = 0 | (xbit << (${size} - 1)) | (res >> ((${size} + 1) - ${size}));"
|
|
echo " }"
|
|
echo " else if (${size} > SHIFTMAX_INT${size}_T"
|
|
echo " && count == 1) {"
|
|
echo " res = (res << 1) | (xbit << (1 - 1)) | 0;"
|
|
echo " }"
|
|
echo " else {"
|
|
echo " res = (res << count) | (xbit << (count - 1)) | (res >> ((${size} + 1) - count));"
|
|
echo " }"
|
|
else
|
|
echo " flags = (res >> (count - 1)) & 1;"
|
|
echo " if (${size} > SHIFTMAX_INT${size}_T"
|
|
echo " && count == ${size}) {"
|
|
echo " res = (res << ((${size} + 1) - ${size})) | (xbit << (${size} - ${size})) | 0;"
|
|
echo " }"
|
|
echo " else if (${size} > SHIFTMAX_INT${size}_T"
|
|
echo " && count == 1) {"
|
|
echo " res = 0 | (xbit << (${size} - 1)) | (res >> 1);"
|
|
echo " }"
|
|
echo " else {"
|
|
echo " res = (res << ((${size} + 1) - count)) | (xbit << (${size} - count)) | (res >> count);"
|
|
echo " }"
|
|
fi
|
|
echo " }"
|
|
echo " flags *= TME_M68K_FLAG_C;"
|
|
echo " flags |= (flags * TME_M68K_FLAG_X);"
|
|
;;
|
|
esac
|
|
echo " }"
|
|
|
|
echo ""
|
|
echo " /* store the result: */"
|
|
echo " TME_M68K_INSN_OP1(tme_${sign}int${size}_t) = res;"
|
|
|
|
echo ""
|
|
echo " /* generate the N flag. we cast to tme_uint8_t as soon as we"
|
|
echo " know the bit we want is within the range of the type, to try"
|
|
echo " to affect the generated assembly: */"
|
|
echo " flags |= ((tme_uint8_t) (((tme_uint${size}_t) res) >> (${size} - 1))) * TME_M68K_FLAG_N;"
|
|
|
|
echo ""
|
|
echo " /* generate the Z flag: */"
|
|
echo " if (res == 0) flags |= TME_M68K_FLAG_Z;"
|
|
|
|
echo ""
|
|
echo " /* store the flags: */"
|
|
echo " ic->tme_m68k_ireg_ccr = flags;"
|
|
echo " TME_M68K_INSN_OK;"
|
|
echo "}"
|
|
done
|
|
done
|
|
|
|
# movep_rm, movep_mr, movem_rm, and movem_mr:
|
|
for name in rm mr; do
|
|
|
|
# movep and movem don't need 8-bit versions:
|
|
if test ${size} = 8; then continue; fi
|
|
|
|
# if we're making the header, just emit declarations:
|
|
if $header; then
|
|
echo "TME_M68K_INSN_DECL(tme_m68k_movep_${name}${size});"
|
|
echo "TME_M68K_INSN_DECL(tme_m68k_movem_${name}${size});"
|
|
continue
|
|
fi
|
|
|
|
# emit the movep function:
|
|
echo ""
|
|
echo "/* the movep_${name} function on a ${size}-bit dreg: */"
|
|
echo "TME_M68K_INSN(tme_m68k_movep_${name}${size})"
|
|
echo "{"
|
|
echo " unsigned int function_code;"
|
|
echo " tme_uint32_t linear_address;"
|
|
if test $name = rm; then
|
|
echo " tme_uint${size}_t value;"
|
|
fi
|
|
echo " int dreg;"
|
|
echo ""
|
|
echo " TME_M68K_INSN_CANFAULT;"
|
|
echo ""
|
|
echo " function_code = TME_M68K_FUNCTION_CODE_DATA(ic);"
|
|
echo " linear_address = TME_M68K_INSN_OP1(tme_uint32_t);"
|
|
echo " linear_address += (tme_int32_t) ((tme_int16_t) TME_M68K_INSN_SPECOP);"
|
|
echo " dreg = TME_M68K_IREG_D0 + TME_FIELD_EXTRACTU(TME_M68K_INSN_OPCODE, 9, 3);"
|
|
|
|
# set value:
|
|
if test $name = rm; then
|
|
echo " value = ic->tme_m68k_ireg_uint${size}(dreg${reg_size_shift});"
|
|
value="value"
|
|
else
|
|
value="ic->tme_m68k_ireg_uint${size}(dreg${reg_size_shift})"
|
|
fi
|
|
|
|
# transfer the bytes:
|
|
pos=${size}
|
|
while test $pos != 0; do
|
|
pos=`expr ${pos} - 8`
|
|
echo " if (!TME_M68K_SEQUENCE_RESTARTING) {"
|
|
echo " ic->_tme_m68k_ea_function_code = function_code;"
|
|
echo " ic->_tme_m68k_ea_address = linear_address;"
|
|
if test $name = rm; then
|
|
echo " ic->tme_m68k_ireg_memx8 = TME_FIELD_EXTRACTU(${value}, ${pos}, 8);"
|
|
echo " }"
|
|
echo " tme_m68k_write_memx8(ic);"
|
|
else
|
|
echo " }"
|
|
echo " tme_m68k_read_memx8(ic);"
|
|
echo " if (!TME_M68K_SEQUENCE_RESTARTING) {"
|
|
echo " TME_FIELD_DEPOSIT${size}(${value}, ${pos}, 8, ic->tme_m68k_ireg_memx8);"
|
|
echo " }"
|
|
fi
|
|
echo " linear_address += 2;"
|
|
done
|
|
|
|
echo " TME_M68K_INSN_OK;"
|
|
echo "}"
|
|
|
|
# emit the movem function:
|
|
echo ""
|
|
echo "/* the movem_${name} function on ${size}-bit registers: */"
|
|
echo "TME_M68K_INSN(tme_m68k_movem_${name}${size})"
|
|
echo "{"
|
|
echo " int ireg, direction;"
|
|
echo " tme_uint16_t mask, bit;"
|
|
echo " unsigned int ea_mode;"
|
|
echo " tme_uint32_t addend;"
|
|
echo " tme_uint32_t total_size;"
|
|
|
|
echo " /* get the register mask, and figure out the total size"
|
|
echo " of the transfer: */"
|
|
echo " mask = TME_M68K_INSN_SPECOP;"
|
|
echo " total_size = 0;"
|
|
echo " if (mask != 0) {"
|
|
echo " TME_M68K_INSN_CANFAULT;"
|
|
echo " bit = mask;"
|
|
echo " do {"
|
|
echo " total_size += sizeof(tme_uint${size}_t);"
|
|
echo " bit &= (bit - 1);"
|
|
echo " } while (bit != 0);"
|
|
echo " }"
|
|
|
|
echo ""
|
|
echo " /* figure out what direction to move in, and where to start from: */"
|
|
echo " ea_mode = TME_FIELD_EXTRACTU(TME_M68K_INSN_OPCODE, 3, 3);"
|
|
echo " direction = 1;"
|
|
echo " ireg = TME_M68K_IREG_D0;"
|
|
if test $name = rm; then
|
|
echo " if (ea_mode == 4) {"
|
|
echo " direction = -1;"
|
|
echo " ireg = TME_M68K_IREG_A7;"
|
|
echo " if (!TME_M68K_SEQUENCE_RESTARTING) {"
|
|
echo ""
|
|
echo " /* \"For the MC68020, MC68030, MC68040, and CPU32, if"
|
|
echo " the addressing register is also moved to memory, the"
|
|
echo " value written is the initial register value decremented "
|
|
echo " by the size of the operation. The MC68000 and MC68010 "
|
|
echo " write the initial register value (not decremented).\" */"
|
|
echo " if (ic->tme_m68k_type >= TME_M68K_M68020) {"
|
|
echo " ic->tme_m68k_ireg_uint32(TME_M68K_IREG_A0"
|
|
echo " + TME_FIELD_EXTRACTU(TME_M68K_INSN_OPCODE, 0, 3))"
|
|
echo " = (ic->_tme_m68k_ea_address - total_size);"
|
|
echo " }"
|
|
echo ""
|
|
echo " /* predecrement the effective address for the first transfer: */"
|
|
echo " ic->_tme_m68k_ea_address -= sizeof(tme_uint${size}_t);"
|
|
echo " }"
|
|
echo " }"
|
|
fi
|
|
echo " addend = (tme_uint32_t) (direction * sizeof(tme_uint${size}_t));"
|
|
|
|
echo ""
|
|
echo " /* do the transfer: */"
|
|
echo " for (bit = 1; bit != 0; bit <<= 1) {"
|
|
echo " if (mask & bit) {"
|
|
if test $name = rm; then
|
|
echo " if (!TME_M68K_SEQUENCE_RESTARTING) {"
|
|
echo " ic->tme_m68k_ireg_memx${size} = ic->tme_m68k_ireg_uint${size}(ireg${reg_size_shift});"
|
|
echo " }"
|
|
echo " tme_m68k_write_memx${size}(ic);"
|
|
echo " if (!TME_M68K_SEQUENCE_RESTARTING) {"
|
|
else
|
|
echo " tme_m68k_read_memx${size}(ic);"
|
|
echo " if (!TME_M68K_SEQUENCE_RESTARTING) {"
|
|
echo -n " ic->tme_m68k_ireg_uint32(ireg) = "
|
|
if test $size = 32; then
|
|
echo "ic->tme_m68k_ireg_memx${size};"
|
|
else
|
|
echo "TME_EXT_S${size}_U32((tme_int${size}_t) ic->tme_m68k_ireg_memx${size});"
|
|
fi
|
|
fi
|
|
echo " ic->_tme_m68k_ea_address += addend;"
|
|
echo " }"
|
|
echo " }"
|
|
echo " ireg += direction;"
|
|
echo " }"
|
|
echo ""
|
|
|
|
# for the predecrement and postincrement modes, update the
|
|
# address register:
|
|
if test $name = rm; then
|
|
echo " /* if this is the predecrement mode, update the address register: */"
|
|
echo " /* \"For the MC68020, MC68030, MC68040, and CPU32, if"
|
|
echo " the addressing register is also moved to memory, the"
|
|
echo " value written is the initial register value decremented "
|
|
echo " by the size of the operation. The MC68000 and MC68010 "
|
|
echo " write the initial register value (not decremented).\" */"
|
|
echo " if (ea_mode == 4"
|
|
echo " && ic->tme_m68k_type < TME_M68K_M68020) {"
|
|
echo " ic->tme_m68k_ireg_uint32(TME_M68K_IREG_A0"
|
|
echo " + TME_FIELD_EXTRACTU(TME_M68K_INSN_OPCODE, 0, 3))"
|
|
echo " = (ic->_tme_m68k_ea_address + sizeof(tme_uint${size}_t));"
|
|
echo " }"
|
|
else
|
|
echo " /* if this is the postincrement mode, update the address register: */"
|
|
echo " if (ea_mode == 3) {"
|
|
echo " ic->tme_m68k_ireg_uint32(TME_M68K_IREG_A0"
|
|
echo " + TME_FIELD_EXTRACTU(TME_M68K_INSN_OPCODE, 0, 3))"
|
|
echo " = ic->_tme_m68k_ea_address;"
|
|
echo " }"
|
|
fi
|
|
|
|
echo " TME_M68K_INSN_OK;"
|
|
echo "}"
|
|
done
|
|
|
|
# chk32 and chk16:
|
|
if test $size != 8; then
|
|
|
|
# if we're making the header, just emit a declaration:
|
|
if $header; then
|
|
echo "TME_M68K_INSN_DECL(tme_m68k_chk${size});"
|
|
else
|
|
echo ""
|
|
echo "/* chk${size}: */"
|
|
echo "TME_M68K_INSN(tme_m68k_chk${size})"
|
|
echo "{"
|
|
echo " if (*((tme_int${size}_t *) _op0) < 0) {"
|
|
echo " ic->tme_m68k_ireg_ccr |= TME_M68K_FLAG_N;"
|
|
echo " ic->tme_m68k_ireg_pc_last = ic->tme_m68k_ireg_pc;"
|
|
echo " ic->tme_m68k_ireg_pc = ic->tme_m68k_ireg_pc_next;"
|
|
echo " TME_M68K_INSN_EXCEPTION(TME_M68K_EXCEPTION_INST(TME_M68K_VECTOR_CHK));"
|
|
echo " }"
|
|
echo " if (*((tme_int${size}_t *) _op0) > *((tme_int${size}_t *) _op1)) {"
|
|
echo " ic->tme_m68k_ireg_ccr &= ~TME_M68K_FLAG_N;"
|
|
echo " ic->tme_m68k_ireg_pc_last = ic->tme_m68k_ireg_pc;"
|
|
echo " ic->tme_m68k_ireg_pc = ic->tme_m68k_ireg_pc_next;"
|
|
echo " TME_M68K_INSN_EXCEPTION(TME_M68K_EXCEPTION_INST(TME_M68K_VECTOR_CHK));"
|
|
echo " }"
|
|
echo " TME_M68K_INSN_OK;"
|
|
echo "}"
|
|
fi
|
|
fi
|
|
|
|
# cas:
|
|
name=cas
|
|
if $header; then
|
|
echo "TME_M68K_INSN_DECL(tme_m68k_${name}${size});"
|
|
else
|
|
echo ""
|
|
echo "/* ${name}${size}: */"
|
|
echo "TME_M68K_INSN(tme_m68k_${name}${size})"
|
|
echo "{"
|
|
echo " struct tme_m68k_rmw rmw;"
|
|
echo " struct tme_m68k_tlb *tlb;"
|
|
echo " int ireg_dc, ireg_du;"
|
|
echo " tme_uint${size}_t value_dc, value_du, value_mem;"
|
|
echo ""
|
|
echo " /* start the read/modify/write cycle: */"
|
|
echo " rmw.tme_m68k_rmw_addresses[0] = ic->_tme_m68k_ea_address;"
|
|
echo " rmw.tme_m68k_rmw_address_count = 1;"
|
|
echo " rmw.tme_m68k_rmw_size = sizeof(tme_uint${size}_t);"
|
|
echo " if (tme_m68k_rmw_start(ic,"
|
|
echo " &rmw)) {"
|
|
echo " TME_M68K_INSN_OK;"
|
|
echo " }"
|
|
echo ""
|
|
echo " /* get the compare and update registers: */"
|
|
echo " ireg_dc = TME_M68K_IREG_D0 + TME_FIELD_EXTRACTU(TME_M68K_INSN_SPECOP, 0, 3);"
|
|
echo " ireg_du = TME_M68K_IREG_D0 + TME_FIELD_EXTRACTU(TME_M68K_INSN_SPECOP, 6, 3);"
|
|
echo ""
|
|
echo " /* if we can do the fast compare-and-exchange: */"
|
|
echo " if (!rmw.tme_m68k_rmw_slow_reads[0]) {"
|
|
echo ""
|
|
echo " /* get the compare and update values in big-endian byte order: */"
|
|
echo " value_dc = ic->tme_m68k_ireg_uint${size}(ireg_dc${reg_size_shift});"
|
|
echo " value_du = ic->tme_m68k_ireg_uint${size}(ireg_du${reg_size_shift});"
|
|
if test ${size} != 8; then
|
|
echo " value_dc = tme_htobe_u${size}(value_dc);"
|
|
echo " value_du = tme_htobe_u${size}(value_du);"
|
|
fi
|
|
echo ""
|
|
echo " /* get this TLB entry: */"
|
|
echo " tlb = rmw.tme_m68k_rmw_tlbs[0];"
|
|
echo ""
|
|
echo " /* this TLB entry must allow fast reading and fast writing"
|
|
echo " to the same memory: */"
|
|
echo " assert (tlb->tme_m68k_tlb_emulator_off_read != TME_EMULATOR_OFF_UNDEF"
|
|
echo " && tlb->tme_m68k_tlb_emulator_off_write == tlb->tme_m68k_tlb_emulator_off_read);"
|
|
echo ""
|
|
echo " /* do the compare-and-exchange: */"
|
|
echo " value_mem ="
|
|
echo " tme_memory_atomic_cx${size}(((tme_shared tme_uint${size}_t *)"
|
|
echo " (tlb->tme_m68k_tlb_emulator_off_read"
|
|
echo " + ic->_tme_m68k_ea_address)),"
|
|
echo " value_dc,"
|
|
echo " value_du,"
|
|
echo " tlb->tme_m68k_tlb_bus_rwlock,"
|
|
echo " sizeof(tme_uint8_t));"
|
|
echo -n " ic->tme_m68k_ireg_memx${size} = "
|
|
if test ${size} != 8; then echo -n "tme_betoh_u${size}"; fi
|
|
echo "(value_mem);"
|
|
echo " }"
|
|
echo ""
|
|
echo " /* compare the compare operand to the effective address operand: */"
|
|
echo " tme_m68k_cmp${size}(ic, &ic->tme_m68k_ireg_uint${size}(ireg_dc${reg_size_shift}), &ic->tme_m68k_ireg_memx${size});"
|
|
echo ""
|
|
echo " /* if the comparison succeeded: */"
|
|
echo " if (ic->tme_m68k_ireg_ccr & TME_M68K_FLAG_Z) {"
|
|
echo ""
|
|
echo " /* write the update operand to the effective address operand: */"
|
|
echo " ic->tme_m68k_ireg_memx${size} = ic->tme_m68k_ireg_uint${size}(ireg_du${reg_size_shift});"
|
|
echo " }"
|
|
echo ""
|
|
echo " /* otherwise, the comparison failed: */"
|
|
echo " else {"
|
|
echo ""
|
|
echo " /* write the effective address operand to the compare operand: */"
|
|
echo " ic->tme_m68k_ireg_uint${size}(ireg_dc${reg_size_shift}) = ic->tme_m68k_ireg_memx${size};"
|
|
echo " }"
|
|
echo ""
|
|
echo " /* finish the read/modify/write cycle: */"
|
|
echo " tme_m68k_rmw_finish(ic,"
|
|
echo " &rmw,"
|
|
echo " (ic->tme_m68k_ireg_ccr & TME_M68K_FLAG_Z) != 0);"
|
|
echo " TME_M68K_INSN_OK;"
|
|
echo "}"
|
|
fi
|
|
|
|
# cas2:
|
|
name=cas2_
|
|
if test $size != 8; then
|
|
|
|
if $header; then
|
|
echo "TME_M68K_INSN_DECL(tme_m68k_${name}${size});"
|
|
else
|
|
echo ""
|
|
echo "/* ${name}${size}: */"
|
|
echo "TME_M68K_INSN(tme_m68k_${name}${size})"
|
|
echo "{"
|
|
echo " struct tme_m68k_rmw rmw;"
|
|
echo " int ireg_dcx, ireg_dux;"
|
|
echo " int ireg_dcy, ireg_duy;"
|
|
echo " const tme_uint16_t specopx = TME_M68K_INSN_SPECOP;"
|
|
echo " const tme_uint16_t specopy = TME_M68K_INSN_OP0(tme_uint16_t);"
|
|
echo ""
|
|
echo " /* start the read/modify/write cycle: */"
|
|
echo " ic->_tme_m68k_ea_function_code = TME_M68K_FUNCTION_CODE_DATA(ic);"
|
|
echo " rmw.tme_m68k_rmw_addresses[0] = ic->tme_m68k_ireg_uint32(TME_M68K_IREG_D0"
|
|
echo " + TME_FIELD_EXTRACTU(specopx, 12, 4));"
|
|
echo " rmw.tme_m68k_rmw_addresses[1] = ic->tme_m68k_ireg_uint32(TME_M68K_IREG_D0"
|
|
echo " + TME_FIELD_EXTRACTU(specopy, 12, 4));"
|
|
echo " rmw.tme_m68k_rmw_address_count = 2;"
|
|
echo " rmw.tme_m68k_rmw_size = sizeof(tme_uint${size}_t);"
|
|
echo " if (tme_m68k_rmw_start(ic,"
|
|
echo " &rmw)) {"
|
|
echo " TME_M68K_INSN_OK;"
|
|
echo " }"
|
|
echo ""
|
|
echo " /* do the comparisons: */"
|
|
echo " ireg_dcx = TME_M68K_IREG_D0 + TME_FIELD_EXTRACTU(specopx, 0, 3);"
|
|
echo " ireg_dcy = TME_M68K_IREG_D0 + TME_FIELD_EXTRACTU(specopy, 0, 3);"
|
|
echo " tme_m68k_cmp${size}(ic,"
|
|
echo " &ic->tme_m68k_ireg_uint${size}(ireg_dcx${reg_size_shift}),"
|
|
echo " &ic->tme_m68k_ireg_memx${size});"
|
|
echo " if (ic->tme_m68k_ireg_ccr & TME_M68K_FLAG_Z) {"
|
|
echo " tme_m68k_cmp${size}(ic,"
|
|
echo " &ic->tme_m68k_ireg_uint${size}(ireg_dcy${reg_size_shift}),"
|
|
echo " &ic->tme_m68k_ireg_memy${size});"
|
|
echo " }"
|
|
echo ""
|
|
echo " /* if the comparisons succeeded: */"
|
|
echo " if (ic->tme_m68k_ireg_ccr & TME_M68K_FLAG_Z) {"
|
|
echo ""
|
|
echo " /* write the update operands to the effective address operands: */"
|
|
echo " ireg_dux = TME_M68K_IREG_D0 + TME_FIELD_EXTRACTU(specopx, 6, 3);"
|
|
echo " ireg_duy = TME_M68K_IREG_D0 + TME_FIELD_EXTRACTU(specopy, 6, 3);"
|
|
echo " ic->tme_m68k_ireg_memx${size} = ic->tme_m68k_ireg_uint${size}(ireg_dux${reg_size_shift});"
|
|
echo " ic->tme_m68k_ireg_memy${size} = ic->tme_m68k_ireg_uint${size}(ireg_duy${reg_size_shift});"
|
|
echo " }"
|
|
echo ""
|
|
echo " /* otherwise, the comparisons failed: */"
|
|
echo " else {"
|
|
echo ""
|
|
echo " /* write the effective address operands to the compare operands."
|
|
echo " \"If Dc1 and Dc2 specify the same data register and the comparison"
|
|
echo " fails, memory operand 1 is stored in the data register.\" */"
|
|
echo " ic->tme_m68k_ireg_uint${size}(ireg_dcy${reg_size_shift}) = ic->tme_m68k_ireg_memy${size};"
|
|
echo " ic->tme_m68k_ireg_uint${size}(ireg_dcx${reg_size_shift}) = ic->tme_m68k_ireg_memx${size};"
|
|
echo " }"
|
|
echo ""
|
|
echo " /* finish the read/modify/write cycle: */"
|
|
echo " tme_m68k_rmw_finish(ic,"
|
|
echo " &rmw,"
|
|
echo " (ic->tme_m68k_ireg_ccr & TME_M68K_FLAG_Z) != 0);"
|
|
echo " TME_M68K_INSN_OK;"
|
|
echo "}"
|
|
fi
|
|
fi
|
|
|
|
# moves:
|
|
if $header; then
|
|
echo "TME_M68K_INSN_DECL(tme_m68k_moves${size});"
|
|
else
|
|
echo ""
|
|
echo "/* moves${size}: */"
|
|
echo "TME_M68K_INSN(tme_m68k_moves${size})"
|
|
echo "{"
|
|
echo " int ireg;"
|
|
echo " tme_uint${size}_t ireg_value;"
|
|
echo " unsigned int ea_reg;"
|
|
echo " unsigned int increment;"
|
|
echo " TME_M68K_INSN_PRIV;"
|
|
echo " TME_M68K_INSN_CANFAULT;"
|
|
echo " ireg = TME_M68K_IREG_D0 + TME_FIELD_EXTRACTU(TME_M68K_INSN_SPECOP, 12, 4);"
|
|
echo ""
|
|
echo " /* in case we're storing the same address register used in a"
|
|
echo " postincrement or predecrement EA, save the current value"
|
|
echo " of the register now: */"
|
|
echo " ireg_value = ic->tme_m68k_ireg_uint${size}(ireg${reg_size_shift});"
|
|
echo ""
|
|
echo " /* we have to handle postincrement and predecrement ourselves: */"
|
|
echo " if (!TME_M68K_SEQUENCE_RESTARTING) {"
|
|
echo " ea_reg = TME_M68K_IREG_A0 + TME_FIELD_EXTRACTU(TME_M68K_INSN_OPCODE, 0, 3);"
|
|
echo " increment = TME_M68K_SIZE_${size};"
|
|
echo " if (increment == TME_M68K_SIZE_8 && ea_reg == TME_M68K_IREG_A7) {"
|
|
echo " increment = TME_M68K_SIZE_16;"
|
|
echo " }"
|
|
echo " switch (TME_FIELD_EXTRACTU(TME_M68K_INSN_OPCODE, 3, 3)) {"
|
|
echo " case 3: ic->tme_m68k_ireg_uint32(ea_reg) += increment; break;"
|
|
echo " case 4: ic->_tme_m68k_ea_address = (ic->tme_m68k_ireg_uint32(ea_reg) -= increment); break;"
|
|
echo " default: break;"
|
|
echo " }"
|
|
echo " }"
|
|
echo ""
|
|
echo " if (TME_M68K_INSN_SPECOP & TME_BIT(11)) {"
|
|
echo " if (!TME_M68K_SEQUENCE_RESTARTING) {"
|
|
echo " ic->tme_m68k_ireg_memx${size} = ireg_value;"
|
|
echo " ic->_tme_m68k_ea_function_code = ic->tme_m68k_ireg_dfc;"
|
|
echo " }"
|
|
echo " tme_m68k_write_memx${size}(ic);"
|
|
echo " }"
|
|
echo " else {"
|
|
echo " if (!TME_M68K_SEQUENCE_RESTARTING) {"
|
|
echo " ic->_tme_m68k_ea_function_code = ic->tme_m68k_ireg_sfc;"
|
|
echo " }"
|
|
echo " tme_m68k_read_memx${size}(ic);"
|
|
if test ${size} != 32; then
|
|
echo " if (ireg >= TME_M68K_IREG_A0) {"
|
|
echo " ic->tme_m68k_ireg_uint32(ireg) = "
|
|
echo " TME_EXT_S${size}_U32((tme_int${size}_t) ic->tme_m68k_ireg_memx${size});"
|
|
echo " }"
|
|
echo " else"
|
|
echo -n " "
|
|
fi
|
|
echo " ic->tme_m68k_ireg_uint${size}(ireg${reg_size_shift}) = ic->tme_m68k_ireg_memx${size};"
|
|
echo " }"
|
|
echo " TME_M68K_INSN_OK;"
|
|
echo "}"
|
|
fi
|
|
done
|
|
|
|
# generate the memory read and write functions:
|
|
|
|
# permute on size:
|
|
for size in 8 16 32 any; do
|
|
|
|
# permute on read or write:
|
|
for name in read write; do
|
|
capname=`echo $name | tr a-z A-Z`
|
|
if test $name = read; then
|
|
from="from"
|
|
else
|
|
from="to"
|
|
fi
|
|
|
|
# permute on the special-purpose what:
|
|
for what in memx mem inst stack; do
|
|
|
|
# placeholder for another permutation:
|
|
:
|
|
|
|
# dispatch on the size:
|
|
_first=_first ; _last=_last
|
|
case "$size" in
|
|
8) _first= ; _last= ;;
|
|
esac
|
|
|
|
# set up the details of each special purpose:
|
|
rval="void"
|
|
args=""
|
|
args_proto=""
|
|
fc=""
|
|
addr=""
|
|
count=""
|
|
tlb="TME_M68K_DTLB_ENTRY(ic, bus_context, function_code, linear_address${_first})"
|
|
flags="TME_M68K_BUS_CYCLE_NORMAL"
|
|
case "${name}-${what}-${size}" in
|
|
*-memx-8 | *-memx-16 | *-memx-32)
|
|
action="${name}_${what}${size}"
|
|
fcptr="&ic->_tme_m68k_ea_function_code"
|
|
addrptr="&ic->_tme_m68k_ea_address"
|
|
reg="ic->tme_m68k_ireg_memx${size}"
|
|
regptr="&${reg}"
|
|
;;
|
|
*-mem-any)
|
|
action="${name}_${what}"
|
|
args_proto=", tme_uint8_t *, unsigned int"
|
|
args=", tme_uint8_t *buffer, unsigned int count"
|
|
fcptr="&ic->_tme_m68k_ea_function_code"
|
|
addrptr="&ic->_tme_m68k_ea_address"
|
|
_last=
|
|
reg=
|
|
regptr="buffer"
|
|
;;
|
|
*-mem-8 | *-mem-16 | *-mem-32)
|
|
action="${name}_${what}${size}"
|
|
args_proto=", int"
|
|
args="${args_proto} ireg"
|
|
fcptr="&ic->_tme_m68k_ea_function_code"
|
|
addrptr="&ic->_tme_m68k_ea_address"
|
|
reg="ic->tme_m68k_ireg_uint${size}(ireg)"
|
|
regptr="&${reg}"
|
|
;;
|
|
read-stack-16 | read-stack-32)
|
|
action="pop${size}"
|
|
args_proto=", tme_uint${size}_t *"
|
|
args="${args_proto}_value"
|
|
fc="TME_M68K_FUNCTION_CODE_DATA(ic)"
|
|
addrptr="&ic->tme_m68k_ireg_a7"
|
|
regptr="_value"
|
|
reg="*${regptr}"
|
|
;;
|
|
write-stack-16 | write-stack-32)
|
|
action="push${size}"
|
|
args_proto=", tme_uint${size}_t "
|
|
args="${args_proto}value"
|
|
fc="TME_M68K_FUNCTION_CODE_DATA(ic)"
|
|
addr="ic->tme_m68k_ireg_a7 - sizeof(tme_uint${size}_t)"
|
|
reg="value"
|
|
regptr="&${reg}"
|
|
;;
|
|
read-inst-16 | read-inst-32)
|
|
rval="tme_uint${size}_t"
|
|
action="fetch${size}"
|
|
args_proto=", tme_uint32_t"
|
|
args="${args_proto} pc"
|
|
fc="TME_M68K_FUNCTION_CODE_PROGRAM(ic)"
|
|
addrptr="&pc"
|
|
tlb="&ic->_tme_m68k_itlb"
|
|
flags="TME_M68K_BUS_CYCLE_FETCH"
|
|
;;
|
|
*)
|
|
continue
|
|
;;
|
|
esac
|
|
|
|
# if we're making the header, just emit a declaration:
|
|
if $header; then
|
|
echo "${rval} tme_m68k_${action} _TME_P((struct tme_m68k *${args_proto}));"
|
|
continue
|
|
fi
|
|
|
|
# start the function:
|
|
echo ""
|
|
echo "/* this ${name}s a ${size}-bit ${what} value: */"
|
|
echo "${rval}"
|
|
echo "tme_m68k_${action}(struct tme_m68k *ic${args}) "
|
|
echo "{"
|
|
|
|
# our locals:
|
|
echo " tme_bus_context_t bus_context = ic->_tme_m68k_bus_context;"
|
|
echo -n " unsigned int function_code = "
|
|
if test "x${fc}" != x; then
|
|
echo "${fc};"
|
|
fc="function_code"
|
|
fcptr="&function_code"
|
|
else
|
|
fc=`echo ${fcptr} | sed -e 's,^&,,'`
|
|
echo "${fc};"
|
|
fi
|
|
echo -n " tme_uint32_t linear_address${_first} = "
|
|
if test "x${addr}" != x; then
|
|
echo "${addr};"
|
|
addr="linear_address${_first}"
|
|
addrptr="&linear_address${_first}"
|
|
else
|
|
addr=`echo ${addrptr} | sed -e 's,^&,,'`
|
|
echo "${addr};"
|
|
fi
|
|
if test "x${count}" = x; then
|
|
if test $size = any; then count=count; else count="sizeof(tme_uint${size}_t)"; fi
|
|
fi
|
|
if test x$_last != x; then
|
|
echo " tme_uint32_t linear_address${_last} = linear_address_first + ${count} - 1;";
|
|
fi
|
|
echo " struct tme_m68k_tlb *tlb = ${tlb};"
|
|
if test $size != any; then
|
|
memtype="tme_uint${size}_t"
|
|
echo " ${memtype} mem_value;"
|
|
memtype="tme_shared ${memtype} *"
|
|
if test $name = read; then memtype="const ${memtype}"; fi
|
|
echo " ${memtype}mem;"
|
|
fi
|
|
case "$what" in
|
|
inst)
|
|
echo " unsigned int fetch_slow_next = ic->_tme_m68k_insn_fetch_slow_next;"
|
|
regptr="((tme_uint${size}_t *) (((tme_uint8_t *) &ic->_tme_m68k_insn_fetch_buffer[0]) + fetch_slow_next))"
|
|
reg="*${regptr}"
|
|
;;
|
|
esac
|
|
|
|
# track statistics:
|
|
echo ""
|
|
echo "#ifdef _TME_M68K_STATS"
|
|
echo " ic->tme_m68k_stats.tme_m68k_stats_memory_total++;"
|
|
echo "#endif /* _TME_M68K_STATS */"
|
|
|
|
# if this is a write, log the value written:
|
|
if test $name = write; then
|
|
echo ""
|
|
echo " /* log the value written: */"
|
|
if test $size != any; then
|
|
echo " tme_m68k_verify_mem${size}(ic, ${fc}, ${addr}, ${reg}, TME_BUS_CYCLE_WRITE);"
|
|
echo " tme_m68k_log(ic, 1000, TME_OK, "
|
|
echo " (TME_M68K_LOG_HANDLE(ic),"
|
|
echo " _(\"${action}\t%d:0x%08x:\t0x%0"`expr ${size} / 4`"x\"),"
|
|
echo " ${fc},"
|
|
echo " ${addr},"
|
|
echo " ${reg}));"
|
|
else
|
|
echo " tme_m68k_verify_mem_any(ic, ${fc}, ${addr}, ${regptr}, ${count}, TME_BUS_CYCLE_WRITE);"
|
|
echo " tme_m68k_log_start(ic, 1000, TME_OK) {"
|
|
echo " unsigned int byte_i;"
|
|
echo " tme_log_part(TME_M68K_LOG_HANDLE(ic),"
|
|
echo " _(\"${action} %d:0x%08x count %d:\"),"
|
|
echo " ${fc},"
|
|
echo " ${addr},"
|
|
echo " ${count});"
|
|
echo " for (byte_i = 0; byte_i < count ; byte_i++) {"
|
|
echo " tme_log_part(TME_M68K_LOG_HANDLE(ic), \" 0x%02x\", (${regptr})[byte_i]);"
|
|
echo " }"
|
|
echo " } tme_m68k_log_finish(ic);"
|
|
fi
|
|
fi
|
|
|
|
echo ""
|
|
echo " /* busy this TLB entry: */"
|
|
echo " tme_m68k_tlb_busy(tlb);"
|
|
|
|
# if this is an any-transfer:
|
|
#
|
|
if test $size = any; then
|
|
echo ""
|
|
echo " /* call the full ${name} function: */"
|
|
echo " tme_m68k_${name}(ic, tlb, ${fcptr}, ${addrptr}, ${regptr}, ${count}, TME_M68K_BUS_CYCLE_RAW);"
|
|
|
|
# otherwise, this is not an any-transfer:
|
|
#
|
|
else
|
|
|
|
# dispatch on the what:
|
|
#
|
|
i=
|
|
case "$what" in
|
|
inst)
|
|
echo ""
|
|
echo " /* if this fetch was done by the fast executor: */"
|
|
echo " if (__tme_predict_true(fetch_slow_next < ic->_tme_m68k_insn_fetch_slow_count_fast)) {"
|
|
echo ""
|
|
echo " /* the entire fetch must be in the instruction buffer, and"
|
|
echo " we must be restarting: */"
|
|
echo " assert ((fetch_slow_next + sizeof(tme_uint${size}_t))"
|
|
echo " <= ic->_tme_m68k_insn_fetch_slow_count_fast);"
|
|
echo " assert (TME_M68K_SEQUENCE_RESTARTING);"
|
|
echo " mem_value = tme_memory_read${size}(${regptr}, sizeof(tme_uint16_t));"
|
|
echo " }"
|
|
echo ""
|
|
echo " /* otherwise, this fetch was not done by the fast executor: */"
|
|
echo " else {"
|
|
echo ""
|
|
echo " /* if we're restarting, but the offset in the instruction buffer"
|
|
echo " to fetch into is at the instruction buffer total, this must be"
|
|
echo " a fake fault caused by the fast executor. we confirm this by"
|
|
echo " checking that this transfer \"caused\" the fault, and that this"
|
|
echo " transfer will be the first slow one after any fast fetches."
|
|
echo " in this case, we can cancel the restart for now: */"
|
|
echo " if (TME_M68K_SEQUENCE_RESTARTING"
|
|
echo " && (fetch_slow_next"
|
|
echo " == ic->_tme_m68k_insn_fetch_slow_count_total)) {"
|
|
echo " assert ((ic->_tme_m68k_sequence._tme_m68k_sequence_transfer_next"
|
|
echo " == ic->_tme_m68k_sequence._tme_m68k_sequence_transfer_faulted)"
|
|
echo " && (fetch_slow_next"
|
|
echo " == ic->_tme_m68k_insn_fetch_slow_count_fast));"
|
|
echo " ic->_tme_m68k_sequence._tme_m68k_sequence_transfer_faulted--;"
|
|
echo " }"
|
|
echo ""
|
|
echo " /* if we're not restarting: */"
|
|
echo " if (!TME_M68K_SEQUENCE_RESTARTING) {"
|
|
echo ""
|
|
echo " /* we advance the instruction buffer total *before* we do"
|
|
echo " what may be a slow fetch, because we may transfer a few"
|
|
echo " bytes and then fault. without this, those few bytes"
|
|
echo " would not get saved in the exception stack frame and"
|
|
echo " restored later before the continuation of the fetch: */"
|
|
echo " ic->_tme_m68k_insn_fetch_slow_count_total += sizeof(tme_uint${size}_t);"
|
|
echo " }"
|
|
echo ""
|
|
echo " /* make sure that if this is a new transfer or if this"
|
|
echo " transfer faulted, that we're fetching for the current"
|
|
echo " last positions in the instruction buffer: */"
|
|
echo " assert ((ic->_tme_m68k_sequence._tme_m68k_sequence_transfer_next"
|
|
echo " < ic->_tme_m68k_sequence._tme_m68k_sequence_transfer_faulted)"
|
|
echo " || ((fetch_slow_next + sizeof(tme_uint${size}_t))"
|
|
echo " == ic->_tme_m68k_insn_fetch_slow_count_total));"
|
|
i=" "
|
|
;;
|
|
esac
|
|
|
|
echo ""
|
|
echo "${i} /* if we aren't restarting, and this address is properly aligned,"
|
|
echo "${i} and this TLB entry covers the operand and allows fast ${name}s: */"
|
|
echo "${i} if (__tme_predict_true(!TME_M68K_SEQUENCE_RESTARTING"
|
|
align_min="sizeof(tme_uint8_t)"
|
|
if test $size != 8; then
|
|
echo -n "${i} && ("
|
|
if test $what = inst; then
|
|
align_min="sizeof(tme_uint16_t)"
|
|
echo -n "(${align_min} - 1)"
|
|
else
|
|
echo -n "ic->_tme_m68k_bus_16bit"
|
|
fi
|
|
echo " & linear_address${_first}) == 0"
|
|
fi
|
|
echo "${i} && tme_m68k_tlb_is_valid(tlb)"
|
|
echo "${i} && tlb->tme_m68k_tlb_bus_context == bus_context"
|
|
echo "${i} && (tlb->tme_m68k_tlb_function_codes_mask"
|
|
echo "${i} & TME_BIT(function_code))"
|
|
echo "${i} && linear_address${_first} >= (tme_bus_addr32_t) tlb->tme_m68k_tlb_linear_first"
|
|
echo "${i} && linear_address${_last} <= (tme_bus_addr32_t) tlb->tme_m68k_tlb_linear_last"
|
|
echo "${i} && tlb->tme_m68k_tlb_emulator_off_${name} != TME_EMULATOR_OFF_UNDEF)) {"
|
|
|
|
echo ""
|
|
echo "${i} /* make the emulator memory pointer: */"
|
|
echo "${i} mem = (${memtype}) (tlb->tme_m68k_tlb_emulator_off_${name} + linear_address${_first});"
|
|
|
|
if test $name = write; then
|
|
if test $size = 8; then
|
|
echo ""
|
|
echo "${i} /* get the value to write: */"
|
|
echo "${i} mem_value = ${reg};"
|
|
else
|
|
echo ""
|
|
echo "${i} /* get the value to write, in big-endian byte order: */"
|
|
echo "${i} mem_value = tme_htobe_u${size}(${reg});"
|
|
fi
|
|
fi
|
|
|
|
echo ""
|
|
echo "${i} /* do the ${size}-bit bus ${name}: */"
|
|
if test $name = read; then
|
|
echo -n "${i} mem_value = tme_memory_bus_${name}${size}(mem"
|
|
else
|
|
echo -n "${i} tme_memory_bus_${name}${size}(mem, mem_value"
|
|
fi
|
|
echo ", tlb->tme_m68k_tlb_bus_rwlock, ${align_min}, sizeof(tme_uint32_t));"
|
|
|
|
if test $name = read; then
|
|
if test $what = inst; then
|
|
echo ""
|
|
echo "${i} /* put the value read, in host byte order: */"
|
|
echo "${i} mem_value = tme_betoh_u${size}(mem_value);"
|
|
echo "${i} tme_memory_write${size}(${regptr}, mem_value, sizeof(tme_uint16_t));"
|
|
elif test $size = 8; then
|
|
echo ""
|
|
echo "${i} /* put the value read: */"
|
|
echo "${i} ${reg} = mem_value;"
|
|
else
|
|
echo ""
|
|
echo "${i} /* put the value read, in host byte order: */"
|
|
echo "${i} ${reg} = tme_betoh_u${size}(mem_value);"
|
|
fi
|
|
fi
|
|
|
|
echo ""
|
|
echo "${i} /* step the transfer count: */"
|
|
echo "${i} TME_M68K_SEQUENCE_TRANSFER_STEP;"
|
|
echo "${i} }"
|
|
|
|
echo ""
|
|
echo "${i} /* otherwise, do the bus cycles the slow way: */"
|
|
echo "${i} else {"
|
|
echo "${i} tme_m68k_${name}${size}(ic, tlb,"
|
|
echo "${i} ${fcptr},"
|
|
echo "${i} ${addrptr},"
|
|
echo "${i} ${regptr},"
|
|
echo "${i} ${flags});"
|
|
if test ${what} = inst; then
|
|
echo "${i} mem_value = tme_memory_read${size}(${regptr}, sizeof(tme_uint16_t));"
|
|
fi
|
|
echo "${i} }"
|
|
fi
|
|
if test "x${i}" != x; then
|
|
echo " }"
|
|
fi
|
|
|
|
echo ""
|
|
echo " /* unbusy this TLB entry: */"
|
|
echo " tme_m68k_tlb_unbusy(tlb);"
|
|
|
|
# if this is a read, log the value read:
|
|
if test $name = read; then
|
|
echo ""
|
|
echo " /* log the value read: */"
|
|
if test $size != any; then
|
|
echo " tme_m68k_verify_mem${size}(ic, ${fc}, ${addr}, ${reg}, TME_BUS_CYCLE_READ);"
|
|
echo " tme_m68k_log(ic, 1000, TME_OK,"
|
|
echo " (TME_M68K_LOG_HANDLE(ic),"
|
|
echo " _(\"${action}\t%d:0x%08x:\t0x%0"`expr ${size} / 4`"x\"),"
|
|
echo " ${fc},"
|
|
echo " ${addr},"
|
|
echo " ${reg}));"
|
|
else
|
|
echo " tme_m68k_verify_mem_any(ic, ${fc}, ${addr}, ${regptr}, ${count}, TME_BUS_CYCLE_READ);"
|
|
echo " tme_m68k_log_start(ic, 1000, TME_OK) {"
|
|
echo " unsigned int byte_i;"
|
|
echo " tme_log_part(TME_M68K_LOG_HANDLE(ic),"
|
|
echo " _(\"${action} %d:0x%08x count %d:\"),"
|
|
echo " ${fc},"
|
|
echo " ${addr},"
|
|
echo " ${count});"
|
|
echo " for (byte_i = 0; byte_i < count ; byte_i++) {"
|
|
echo " tme_log_part(TME_M68K_LOG_HANDLE(ic), \" 0x%02x\", (${regptr})[byte_i]);"
|
|
echo " }"
|
|
echo " } tme_m68k_log_finish(ic);"
|
|
fi
|
|
fi
|
|
|
|
# perform any updating and value returning:
|
|
case "$what" in
|
|
stack)
|
|
if test $name = read; then dir="+"; else dir="-"; fi
|
|
echo " if (!TME_M68K_SEQUENCE_RESTARTING) {"
|
|
echo " ic->tme_m68k_ireg_a7 ${dir}= sizeof(tme_uint${size}_t);"
|
|
echo " }"
|
|
;;
|
|
inst)
|
|
echo ""
|
|
echo " /* advance the offset in the instruction buffer for the next slow fetch: */"
|
|
echo " fetch_slow_next += sizeof(tme_uint${size}_t);"
|
|
echo " ic->_tme_m68k_insn_fetch_slow_next = fetch_slow_next;"
|
|
echo ""
|
|
echo " /* return the fetched value: */"
|
|
echo " return(mem_value);"
|
|
;;
|
|
esac
|
|
|
|
echo "}"
|
|
:
|
|
done
|
|
|
|
# the general-purpose cycle-making read and write macros:
|
|
if test ${size} != any; then
|
|
|
|
# if we're making the header, emit a macro:
|
|
if $header; then
|
|
echo "#define tme_m68k_${name}${size}(ic, t, fc, la, _v, f) \\"
|
|
echo " tme_m68k_${name}(ic, t, fc, la, (tme_uint8_t *) (_v), sizeof(tme_uint${size}_t), f)"
|
|
fi
|
|
else
|
|
|
|
# if we're making the header, just emit a declaration:
|
|
if $header; then
|
|
echo "void tme_m68k_${name} _TME_P((struct tme_m68k *, struct tme_m68k_tlb *, unsigned int *, tme_uint32_t *, tme_uint8_t *, unsigned int, unsigned int));"
|
|
continue
|
|
fi
|
|
|
|
echo ""
|
|
echo "/* this ${name}s a region of address space using actual bus cycles: */"
|
|
echo "void"
|
|
echo "tme_m68k_${name}(struct tme_m68k *ic, "
|
|
echo " struct tme_m68k_tlb *tlb,"
|
|
echo " unsigned int *_function_code, "
|
|
echo " tme_uint32_t *_linear_address, "
|
|
echo " tme_uint8_t *reg,"
|
|
echo " unsigned int reg_size,"
|
|
echo " unsigned int flags)"
|
|
echo "{"
|
|
|
|
# our locals:
|
|
echo " unsigned int function_code;"
|
|
echo " tme_uint32_t linear_address;"
|
|
echo " tme_bus_addr_t physical_address;"
|
|
echo " int shift;"
|
|
echo " struct tme_bus_cycle cycle;"
|
|
echo " unsigned int transferred, resid, cycle_size;"
|
|
echo " int exception;"
|
|
echo " int err;"
|
|
echo " tme_uint8_t *reg_p;"
|
|
echo " unsigned int buffer_i;"
|
|
echo " tme_uint8_t reg_buffer[sizeof(tme_uint32_t) * 2];"
|
|
if test ${name} = read; then name_const_mem="const "; else name_const_mem= ; fi
|
|
echo " ${name_const_mem}tme_shared tme_uint8_t *mem;"
|
|
|
|
echo ""
|
|
echo " /* if we're not restarting, everything is fresh: */"
|
|
echo " if (!TME_M68K_SEQUENCE_RESTARTING) {"
|
|
echo " function_code = *_function_code;"
|
|
echo " linear_address = *_linear_address;"
|
|
echo " transferred = 0;"
|
|
echo " }"
|
|
|
|
echo ""
|
|
echo " /* otherwise, if this is the transfer that faulted, restore"
|
|
echo " our state to the cycle that faulted, then take into account"
|
|
echo " any data provided by a software rerun of the faulted cycle: */"
|
|
echo " else if (ic->_tme_m68k_sequence._tme_m68k_sequence_transfer_faulted"
|
|
echo " == ic->_tme_m68k_sequence._tme_m68k_sequence_transfer_next) {"
|
|
echo " function_code = *_function_code = ic->_tme_m68k_group0_function_code;"
|
|
echo " linear_address = ic->_tme_m68k_group0_address;"
|
|
echo " transferred = ic->_tme_m68k_sequence._tme_m68k_sequence_transfer_faulted_after;"
|
|
echo " if (transferred >= reg_size) abort();"
|
|
echo " *_linear_address = linear_address - transferred;"
|
|
echo " resid = reg_size - transferred;"
|
|
echo " if (ic->_tme_m68k_group0_buffer_${name}_size > resid) abort();"
|
|
echo " if (ic->_tme_m68k_group0_buffer_${name}_softrr > resid) abort();"
|
|
if test $name = read; then cmp=">"; else cmp="=="; fi
|
|
echo " if (ic->_tme_m68k_group0_buffer_${name}_softrr ${cmp} 0) {"
|
|
echo "#ifdef WORDS_BIGENDIAN"
|
|
echo " memcpy(reg + transferred, "
|
|
echo " ic->_tme_m68k_group0_buffer_${name},"
|
|
echo " ic->_tme_m68k_group0_buffer_${name}_size);"
|
|
echo "#else /* !WORDS_BIGENDIAN */"
|
|
echo " reg_p = (reg + reg_size - 1) - transferred;"
|
|
echo " for (buffer_i = 0;"
|
|
echo " buffer_i < ic->_tme_m68k_group0_buffer_${name}_size;"
|
|
echo " buffer_i++) {"
|
|
echo " *(reg_p--) = ic->_tme_m68k_group0_buffer_${name}[buffer_i];"
|
|
echo " }"
|
|
echo "#endif /* !WORDS_BIGENDIAN */"
|
|
echo " }"
|
|
echo " transferred += ic->_tme_m68k_group0_buffer_${name}_softrr;"
|
|
echo " }"
|
|
|
|
echo ""
|
|
echo " /* otherwise, a later transfer has faulted. just step the"
|
|
echo " transfer number and return: */"
|
|
echo " else {"
|
|
echo " TME_M68K_SEQUENCE_TRANSFER_STEP;"
|
|
echo " return;"
|
|
echo " }"
|
|
|
|
echo ""
|
|
echo " /* do as many bus cycles as needed to complete the transfer: */"
|
|
echo " exception = TME_M68K_EXCEPTION_NONE;"
|
|
echo " cycle_size = 0;"
|
|
echo " for(; transferred < reg_size; ) {"
|
|
echo " resid = reg_size - transferred;"
|
|
|
|
echo ""
|
|
echo " /* start the bus cycle structure: */"
|
|
echo " cycle.tme_bus_cycle_type = TME_BUS_CYCLE_${capname};"
|
|
echo " if (TME_ENDIAN_NATIVE == TME_ENDIAN_BIG"
|
|
echo " || (flags & TME_M68K_BUS_CYCLE_RAW)) {"
|
|
echo " cycle.tme_bus_cycle_buffer = reg + transferred;"
|
|
echo " cycle.tme_bus_cycle_buffer_increment = 1;"
|
|
echo " }"
|
|
echo " else {"
|
|
echo " cycle.tme_bus_cycle_buffer = reg + reg_size - (1 + transferred);"
|
|
echo " cycle.tme_bus_cycle_buffer_increment = -1;"
|
|
echo " }"
|
|
|
|
echo ""
|
|
echo " /* if we're emulating a CPU with a 16-bit bus interface: */"
|
|
echo " if (ic->_tme_m68k_bus_16bit) {"
|
|
echo ""
|
|
echo " /* if we're trying to transfer a non-power-of-two"
|
|
echo " number of bytes, either the CPU is broken (no"
|
|
echo " instructions ever transfer a non-power-of-two"
|
|
echo " number of bytes), or this function allowed an"
|
|
echo " unaligned transfer: */"
|
|
echo " assert((resid & (resid - 1)) == 0"
|
|
echo " || (flags & TME_M68K_BUS_CYCLE_RAW));"
|
|
echo ""
|
|
echo " /* only byte transfers can be unaligned: */"
|
|
echo " if (resid > sizeof(tme_uint8_t)"
|
|
echo " && (linear_address & 1)) {"
|
|
echo " exception = TME_M68K_EXCEPTION_AERR;"
|
|
echo " break;"
|
|
echo " }"
|
|
echo ""
|
|
echo " /* set the bus-size specific parts of the bus cycle structure: */"
|
|
echo " cycle_size = TME_MIN(resid, sizeof(tme_uint16_t));"
|
|
echo " cycle.tme_bus_cycle_size = cycle_size;"
|
|
echo " cycle.tme_bus_cycle_port = TME_BUS_CYCLE_PORT(0, TME_BUS16_LOG2);"
|
|
echo " cycle.tme_bus_cycle_lane_routing = "
|
|
echo " &tme_m68k_router_16[TME_M68K_BUS_ROUTER_INDEX(TME_BUS16_LOG2, cycle_size, linear_address)];"
|
|
echo " }"
|
|
echo ""
|
|
echo " /* otherwise we're emulating a CPU with a 32-bit bus interface: */"
|
|
echo " else {"
|
|
if test $name = read; then
|
|
echo ""
|
|
echo " /* an instruction fetch must be aligned: */"
|
|
echo " if (flags & TME_M68K_BUS_CYCLE_FETCH) {"
|
|
echo " if (linear_address & 1) {"
|
|
echo " exception = TME_M68K_EXCEPTION_AERR;"
|
|
echo " break;"
|
|
echo " }"
|
|
echo " assert(!(resid & 1));"
|
|
echo " }"
|
|
fi
|
|
echo ""
|
|
echo " /* set the bus-size specific parts of the bus cycle structure: */"
|
|
echo " cycle_size = TME_MIN(resid, sizeof(tme_uint32_t) - (linear_address & (sizeof(tme_uint32_t) - 1)));"
|
|
echo " cycle.tme_bus_cycle_size = cycle_size;"
|
|
echo " cycle.tme_bus_cycle_port = TME_BUS_CYCLE_PORT(0, TME_BUS32_LOG2);"
|
|
echo " cycle.tme_bus_cycle_lane_routing = "
|
|
echo " &tme_m68k_router_32[TME_M68K_BUS_ROUTER_INDEX(TME_BUS32_LOG2, cycle_size, linear_address)];"
|
|
echo " }"
|
|
|
|
echo ""
|
|
echo " /* loop while this TLB entry is invalid or does not apply: */"
|
|
echo " for (; __tme_predict_false(tme_m68k_tlb_is_invalid(tlb)"
|
|
echo " || tlb->tme_m68k_tlb_bus_context != ic->_tme_m68k_bus_context"
|
|
echo " || (tlb->tme_m68k_tlb_function_codes_mask & TME_BIT(function_code)) == 0"
|
|
echo " || linear_address < (tme_bus_addr32_t) tlb->tme_m68k_tlb_linear_first"
|
|
echo " || linear_address > (tme_bus_addr32_t) tlb->tme_m68k_tlb_linear_last"
|
|
echo " || (tlb->tme_m68k_tlb_emulator_off_${name} == TME_EMULATOR_OFF_UNDEF"
|
|
echo " && (tlb->tme_m68k_tlb_cycles_ok & TME_BUS_CYCLE_${capname}) == 0)); ) {"
|
|
echo ""
|
|
echo " /* this must not be part of a read/modify/write cycle: */"
|
|
echo " assert(!(flags & TME_M68K_BUS_CYCLE_RMW));"
|
|
echo ""
|
|
echo " /* fill this TLB entry: */"
|
|
echo " tme_m68k_tlb_fill(ic, tlb,"
|
|
echo " function_code,"
|
|
echo " linear_address,"
|
|
echo " TME_BUS_CYCLE_${capname});"
|
|
echo " }"
|
|
echo ""
|
|
echo " /* if this TLB entry allows for fast ${name}s: */"
|
|
echo " mem = tlb->tme_m68k_tlb_emulator_off_${name};"
|
|
echo " if (__tme_predict_true(mem != TME_EMULATOR_OFF_UNDEF)) {"
|
|
echo ""
|
|
echo " /* make the emulator memory pointer: */"
|
|
echo " mem += linear_address;"
|
|
echo ""
|
|
echo " /* limit the cycle size to addresses covered by the TLB entry: */"
|
|
echo " if (__tme_predict_false((cycle_size - 1)"
|
|
echo " > (((tme_bus_addr32_t) tlb->tme_m68k_tlb_linear_last) - linear_address))) {"
|
|
echo " cycle_size = (((tme_bus_addr32_t) tlb->tme_m68k_tlb_linear_last) - linear_address) + 1;"
|
|
echo " }"
|
|
echo ""
|
|
echo " /* if this is a little-endian host, and this isn't a raw ${name}: */"
|
|
echo " if (TME_ENDIAN_NATIVE == TME_ENDIAN_LITTLE"
|
|
echo " && (flags & TME_M68K_BUS_CYCLE_RAW) == 0) {"
|
|
if test ${name} = write; then
|
|
echo ""
|
|
echo " /* byteswap the data to write in the intermediate buffer: */"
|
|
echo " reg_p = cycle.tme_bus_cycle_buffer;"
|
|
echo " buffer_i = 0;"
|
|
echo " do {"
|
|
echo " reg_buffer[buffer_i] = *(reg_p--);"
|
|
echo " } while (++buffer_i != cycle_size);"
|
|
fi
|
|
echo ""
|
|
echo " /* use the intermediate buffer for the ${name}: */"
|
|
echo " cycle.tme_bus_cycle_buffer = ®_buffer[0];"
|
|
echo " }"
|
|
echo ""
|
|
echo " /* do the bus ${name}: */"
|
|
echo " tme_memory_bus_${name}_buffer(mem,"
|
|
echo " cycle.tme_bus_cycle_buffer,"
|
|
echo " cycle_size,"
|
|
echo " tlb->tme_m68k_tlb_bus_rwlock,"
|
|
echo " sizeof(tme_uint8_t),"
|
|
echo " sizeof(tme_uint32_t));"
|
|
if test ${name} = read; then
|
|
echo ""
|
|
echo " /* if this is a little-endian host, and this isn't a raw ${name}: */"
|
|
echo " if (TME_ENDIAN_NATIVE == TME_ENDIAN_LITTLE"
|
|
echo " && (flags & TME_M68K_BUS_CYCLE_RAW) == 0) {"
|
|
echo ""
|
|
echo " /* byteswap the read data in the intermediate buffer: */"
|
|
echo " reg_p = reg + reg_size - (1 + transferred);"
|
|
echo " buffer_i = 0;"
|
|
echo " do {"
|
|
echo " *(reg_p--) = reg_buffer[buffer_i];"
|
|
echo " } while (++buffer_i != cycle_size);"
|
|
echo " }"
|
|
fi
|
|
echo ""
|
|
echo " /* update: */"
|
|
echo " linear_address += cycle_size;"
|
|
echo " transferred += cycle_size;"
|
|
echo " continue;"
|
|
echo " }"
|
|
echo ""
|
|
echo " /* otherwise, this TLB entry does not allow for fast ${name}s: */"
|
|
echo ""
|
|
echo " /* if this is a part of a read/modify/write cycle: */"
|
|
echo " if (flags & TME_M68K_BUS_CYCLE_RMW) {"
|
|
echo ""
|
|
if test ${name} = read; then
|
|
echo " /* if this is the first cycle in this read,"
|
|
echo " we will establish the new lock, otherwise"
|
|
echo " we will continue using the existing lock: */"
|
|
else
|
|
echo " /* we will continue using the existing lock."
|
|
echo " the device will automatically unlock after"
|
|
echo " the last cycle of this write: */"
|
|
fi
|
|
echo " cycle.tme_bus_cycle_type"
|
|
echo " |= (TME_BUS_CYCLE_LOCK"
|
|
echo -n " | ("
|
|
if test ${name} = read; then
|
|
echo -n "transferred == 0 ? 0 : "
|
|
fi
|
|
echo "TME_BUS_CYCLE_UNLOCK));"
|
|
echo " }"
|
|
|
|
echo ""
|
|
echo " /* form the physical address for the bus cycle handler: */"
|
|
echo " physical_address = tlb->tme_m68k_tlb_addr_offset + linear_address;"
|
|
echo " shift = tlb->tme_m68k_tlb_addr_shift;"
|
|
echo " if (shift < 0) {"
|
|
echo " physical_address <<= (0 - shift);"
|
|
echo " }"
|
|
echo " else if (shift > 0) {"
|
|
echo " physical_address >>= shift;"
|
|
echo " }"
|
|
echo " cycle.tme_bus_cycle_address = physical_address;"
|
|
|
|
echo ""
|
|
echo " /* run the bus cycle: */"
|
|
echo " tme_m68k_tlb_unbusy(tlb);"
|
|
echo " tme_m68k_callout_unlock(ic);"
|
|
echo " err = (*tlb->tme_m68k_tlb_bus_tlb.tme_bus_tlb_cycle)"
|
|
echo " (tlb->tme_m68k_tlb_bus_tlb.tme_bus_tlb_cycle_private, &cycle);"
|
|
echo " tme_m68k_callout_relock(ic);"
|
|
echo " tme_m68k_tlb_busy(tlb);"
|
|
echo ""
|
|
echo " /* if the TLB entry was invalidated before the ${name}: */"
|
|
echo " if (err == EBADF"
|
|
echo " && tme_m68k_tlb_is_invalid(tlb)) {"
|
|
echo " cycle.tme_bus_cycle_size = 0;"
|
|
echo " }"
|
|
echo ""
|
|
echo " /* otherwise, if we didn't get a bus error, but some"
|
|
echo " synchronous event has happened: */"
|
|
echo " else if (err == TME_BUS_CYCLE_SYNCHRONOUS_EVENT) {"
|
|
echo ""
|
|
echo " /* after the currently executing instruction finishes, check"
|
|
echo " for external resets, halts, or interrupts: */"
|
|
echo " ic->_tme_m68k_instruction_burst_remaining = 0;"
|
|
echo " }"
|
|
echo ""
|
|
echo " /* otherwise, any other error might be a bus error: */"
|
|
echo " else if (err != TME_OK) {"
|
|
echo " err = tme_bus_tlb_fault(&tlb->tme_m68k_tlb_bus_tlb, &cycle, err);"
|
|
echo " if (err != TME_OK) {"
|
|
echo " exception = TME_M68K_EXCEPTION_BERR;"
|
|
echo " break;"
|
|
echo " }"
|
|
echo " }"
|
|
echo ""
|
|
echo " /* update: */"
|
|
echo " linear_address += cycle.tme_bus_cycle_size;"
|
|
echo " transferred += cycle.tme_bus_cycle_size;"
|
|
echo " }"
|
|
|
|
echo ""
|
|
echo " /* NB: there is no need to explicitly unlock"
|
|
echo " a device. if a locked bus cycle to a device"
|
|
echo " faults, the lock must be automatically unlocked: */"
|
|
|
|
echo ""
|
|
echo " /* if we faulted, stash the information the fault stacker"
|
|
echo " will need and start exception processing: */"
|
|
echo " if (exception != TME_M68K_EXCEPTION_NONE) {"
|
|
echo -n " ic->_tme_m68k_group0_flags = flags"
|
|
if test $name = read; then
|
|
echo -n " | TME_M68K_BUS_CYCLE_READ"
|
|
fi
|
|
echo ";"
|
|
echo " ic->_tme_m68k_group0_function_code = function_code;"
|
|
echo " ic->_tme_m68k_group0_address = linear_address;"
|
|
echo " ic->_tme_m68k_group0_sequence = ic->_tme_m68k_sequence;"
|
|
echo " ic->_tme_m68k_group0_sequence._tme_m68k_sequence_transfer_faulted_after = transferred;"
|
|
echo " ic->_tme_m68k_group0_buffer_${name}_size = cycle_size;"
|
|
if test $name = write; then
|
|
echo "#ifdef WORDS_BIGENDIAN"
|
|
echo " memcpy(ic->_tme_m68k_group0_buffer_${name},"
|
|
echo " reg + transferred,"
|
|
echo " ic->_tme_m68k_group0_buffer_${name}_size);"
|
|
echo "#else /* !WORDS_BIGENDIAN */"
|
|
echo " reg_p = (reg + reg_size - 1) - transferred;"
|
|
echo " for (buffer_i = 0;"
|
|
echo " buffer_i < ic->_tme_m68k_group0_buffer_${name}_size;"
|
|
echo " buffer_i++) {"
|
|
echo " ic->_tme_m68k_group0_buffer_${name}[buffer_i] = *(reg_p--);"
|
|
echo " }"
|
|
echo "#endif /* !WORDS_BIGENDIAN */"
|
|
fi
|
|
echo " if (ic->_tme_m68k_group0_hook != NULL) {"
|
|
echo " (*ic->_tme_m68k_group0_hook)(ic);"
|
|
echo " }"
|
|
echo " ic->_tme_m68k_group0_sequence._tme_m68k_sequence_transfer_faulted = ";
|
|
echo " ic->_tme_m68k_group0_sequence._tme_m68k_sequence_transfer_next;"
|
|
echo " tme_m68k_tlb_unbusy(tlb);"
|
|
echo " tme_m68k_exception(ic, exception);"
|
|
echo " }"
|
|
|
|
echo ""
|
|
echo " /* otherwise, this transfer has now completed: */"
|
|
echo " TME_M68K_SEQUENCE_TRANSFER_STEP;"
|
|
|
|
echo "}"
|
|
fi
|
|
done
|
|
|
|
done
|
|
|
|
# generate the BCD math functions:
|
|
for name in abcd sbcd nbcd; do
|
|
|
|
# if we're making the header, just emit a declaration:
|
|
if $header; then
|
|
echo "TME_M68K_INSN_DECL(tme_m68k_${name});"
|
|
continue
|
|
fi
|
|
|
|
# emit the function:
|
|
echo ""
|
|
echo "TME_M68K_INSN(tme_m68k_${name})"
|
|
echo "{"
|
|
echo " tme_uint8_t dst, dst_msd, dst_lsd;"
|
|
echo " tme_uint8_t src, src_msd, src_lsd;"
|
|
echo " tme_uint8_t res, res_msd, res_lsd;"
|
|
echo " tme_uint8_t flags;"
|
|
|
|
# get the operands:
|
|
if test $name != nbcd; then
|
|
echo " int memory;"
|
|
echo " int rx, ry, function_code;"
|
|
echo ""
|
|
echo " /* load the operands: */"
|
|
echo " rx = TME_FIELD_EXTRACTU(TME_M68K_INSN_OPCODE, 0, 3);"
|
|
echo " ry = TME_FIELD_EXTRACTU(TME_M68K_INSN_OPCODE, 9, 3);"
|
|
echo " memory = (TME_M68K_INSN_OPCODE & TME_BIT(3)) != 0;"
|
|
echo " function_code = TME_M68K_FUNCTION_CODE_DATA(ic);"
|
|
echo " if (memory) {"
|
|
echo " TME_M68K_INSN_CANFAULT;"
|
|
echo " if (!TME_M68K_SEQUENCE_RESTARTING) {"
|
|
# the stack pointer must always be decremented by a multiple of two.
|
|
# assuming rx < 8, ((rx + 1) >> 3) == 1 iff rx == 7, meaning %a7:
|
|
echo " ic->tme_m68k_ireg_uint32(TME_M68K_IREG_A0 + rx) -= sizeof(tme_uint8_t) + ((rx + 1) >> 3);"
|
|
echo " ic->_tme_m68k_ea_function_code = function_code;"
|
|
echo " ic->_tme_m68k_ea_address = ic->tme_m68k_ireg_uint32(TME_M68K_IREG_A0 + rx);"
|
|
echo " }"
|
|
echo " tme_m68k_read_memx8(ic);"
|
|
echo " if (!TME_M68K_SEQUENCE_RESTARTING) {"
|
|
# the stack pointer must always be incremented by a multiple of two.
|
|
# assuming rx < 8, ((rx + 1) >> 3) == 1 iff rx == 7, meaning %a7:
|
|
echo " ic->tme_m68k_ireg_uint32(TME_M68K_IREG_A0 + ry) -= sizeof(tme_uint8_t) + ((ry + 1) >> 3);"
|
|
echo " ic->_tme_m68k_ea_function_code = function_code;"
|
|
echo " ic->_tme_m68k_ea_address = ic->tme_m68k_ireg_uint32(TME_M68K_IREG_A0 + ry);"
|
|
echo " }"
|
|
echo " tme_m68k_read_mem8(ic, TME_M68K_IREG_MEMY32);"
|
|
echo " src = ic->tme_m68k_ireg_memx8;"
|
|
echo " dst = ic->tme_m68k_ireg_memy8;"
|
|
echo " }"
|
|
echo " else {"
|
|
echo " src = ic->tme_m68k_ireg_uint8(rx << 2);"
|
|
echo " dst = ic->tme_m68k_ireg_uint8(ry << 2);"
|
|
echo " }"
|
|
else
|
|
echo ""
|
|
echo " dst = 0x00;"
|
|
echo " src = TME_M68K_INSN_OP1(tme_uint8_t);"
|
|
fi
|
|
echo " dst_lsd = TME_FIELD_EXTRACTU(dst, 0, 4);"
|
|
echo " dst_msd = TME_FIELD_EXTRACTU(dst, 4, 4);"
|
|
echo " src_lsd = TME_FIELD_EXTRACTU(src, 0, 4);"
|
|
echo " src_msd = TME_FIELD_EXTRACTU(src, 4, 4);"
|
|
|
|
# perform the operation:
|
|
echo ""
|
|
echo " /* perform the operation: */"
|
|
if test $name = abcd; then op='+' ; opc='-' ; else op='-' ; opc='+' ; fi
|
|
echo " res_lsd = dst_lsd ${op} src_lsd ${op} ((ic->tme_m68k_ireg_ccr & TME_M68K_FLAG_X) != 0);"
|
|
echo " res_msd = dst_msd ${op} src_msd;"
|
|
echo " flags = 0;"
|
|
echo " if (res_lsd > 9) {"
|
|
echo " res_lsd ${opc}= 10;"
|
|
echo " res_msd ${op}= 1;"
|
|
echo " }"
|
|
echo " if (res_msd > 9) {"
|
|
echo " res_msd ${opc}= 10;"
|
|
echo " flags |= TME_M68K_FLAG_C | TME_M68K_FLAG_X;"
|
|
echo " }"
|
|
echo " res = (res_msd << 4) + (res_lsd & 0xf);"
|
|
echo " if (res == 0) flags |= TME_M68K_FLAG_N;"
|
|
echo ""
|
|
|
|
# store the result
|
|
echo " /* store the result and set the flags: */"
|
|
if test $name != nbcd; then
|
|
echo " if (memory) {"
|
|
echo " if (!TME_M68K_SEQUENCE_RESTARTING) {"
|
|
echo " ic->tme_m68k_ireg_memx8 = res;"
|
|
echo " ic->_tme_m68k_ea_function_code = function_code;"
|
|
echo " ic->_tme_m68k_ea_address = ic->tme_m68k_ireg_uint32(TME_M68K_IREG_A0 + ry);"
|
|
echo " ic->tme_m68k_ireg_ccr = flags;"
|
|
echo " }"
|
|
echo " tme_m68k_write_memx8(ic);"
|
|
echo " }"
|
|
echo " else {"
|
|
echo " ic->tme_m68k_ireg_uint8(ry << 2) = res;"
|
|
echo " ic->tme_m68k_ireg_ccr = flags;"
|
|
echo " }"
|
|
else
|
|
echo " TME_M68K_INSN_OP1(tme_uint8_t) = res;"
|
|
echo " ic->tme_m68k_ireg_ccr = flags;"
|
|
fi
|
|
echo ""
|
|
echo " TME_M68K_INSN_OK;"
|
|
echo "}"
|
|
done
|
|
|
|
# generate the ccr and sr functions:
|
|
for reg in ccr sr; do
|
|
for name in ori andi eori move_to; do
|
|
if test $reg = ccr; then size=8 ; else size=16 ; fi
|
|
|
|
# if we're making the header, just emit a declaration:
|
|
if $header; then
|
|
echo "TME_M68K_INSN_DECL(tme_m68k_${name}_${reg});"
|
|
continue
|
|
fi
|
|
|
|
# emit the function:
|
|
echo ""
|
|
echo "TME_M68K_INSN(tme_m68k_${name}_${reg})"
|
|
echo "{"
|
|
echo " tme_uint${size}_t reg;"
|
|
|
|
# form the new register value:
|
|
src=0
|
|
echo -n " reg = "
|
|
case $name in
|
|
ori) echo -n "ic->tme_m68k_ireg_${reg} | " ;;
|
|
andi) echo -n "ic->tme_m68k_ireg_${reg} & " ;;
|
|
eori) echo -n "ic->tme_m68k_ireg_${reg} ^ " ;;
|
|
move_to) size=16 ; src=1 ;;
|
|
esac
|
|
echo "(TME_M68K_INSN_OP${src}(tme_uint${size}_t) & TME_M68K_FLAG_"`echo $reg | tr a-z A-Z`");"
|
|
|
|
# sr changes are special:
|
|
if test $reg = sr; then
|
|
echo " TME_M68K_INSN_PRIV;"
|
|
echo " TME_M68K_INSN_CHANGE_SR(reg);"
|
|
else
|
|
echo " ic->tme_m68k_ireg_${reg} = reg;"
|
|
fi
|
|
|
|
echo " TME_M68K_INSN_OK;"
|
|
echo "}"
|
|
done
|
|
done
|
|
|
|
# generate the multiply and divide instructions:
|
|
|
|
# permute on signed vs. unsigned:
|
|
for _sign in u s; do
|
|
if test $_sign = u; then sign=u; else sign=; fi
|
|
|
|
# permute on short vs. long:
|
|
for size in s l; do
|
|
if test $size = s; then
|
|
_size=
|
|
small=16
|
|
large=32
|
|
reg_size_shift=' << 1'
|
|
else
|
|
_size=l
|
|
small=32
|
|
large=64
|
|
reg_size_shift=
|
|
fi
|
|
|
|
# if we're making the header, just emit declarations:
|
|
if $header; then
|
|
echo "TME_M68K_INSN_DECL(tme_m68k_mul${_sign}${_size});"
|
|
echo "TME_M68K_INSN_DECL(tme_m68k_div${_sign}${_size});"
|
|
continue
|
|
fi
|
|
|
|
# emit the multiply function:
|
|
echo ""
|
|
echo "TME_M68K_INSN(tme_m68k_mul${_sign}${_size})"
|
|
echo "{"
|
|
if test $large = 64; then
|
|
echo "#ifndef TME_HAVE_INT${large}_T"
|
|
echo " abort();"
|
|
echo "#else /* TME_HAVE_INT${large}_T */"
|
|
echo " unsigned int flag_v;"
|
|
echo " int ireg_dh;"
|
|
fi
|
|
echo " int ireg_dl;"
|
|
echo " tme_${sign}int${large}_t res;"
|
|
echo " tme_uint8_t flags;"
|
|
|
|
echo ""
|
|
echo " /* get the register containing the factor: */"
|
|
echo -n " ireg_dl = TME_M68K_IREG_D0 + "
|
|
if test $size = s; then
|
|
echo "TME_M68K_INSN_OP0(tme_uint32_t);"
|
|
else
|
|
echo "TME_FIELD_EXTRACTU(TME_M68K_INSN_SPECOP, 12, 3);"
|
|
fi
|
|
|
|
echo ""
|
|
echo " /* perform the multiplication: */"
|
|
echo " res = (((tme_${sign}int${large}_t) ic->tme_m68k_ireg_${sign}int${small}(ireg_dl${reg_size_shift}))"
|
|
echo " * TME_M68K_INSN_OP1(tme_${sign}int${small}_t));"
|
|
|
|
echo ""
|
|
echo " /* store the result: */"
|
|
echo " ic->tme_m68k_ireg_${sign}int32(ireg_dl) = (tme_${sign}int32_t) res;"
|
|
if test $large = 64; then
|
|
echo " flag_v = TME_M68K_FLAG_V;"
|
|
echo " if (TME_M68K_INSN_SPECOP & TME_BIT(10)) {"
|
|
echo " flag_v = 0;"
|
|
echo " ireg_dh = TME_M68K_IREG_D0 + TME_FIELD_EXTRACTU(TME_M68K_INSN_SPECOP, 0, 3);"
|
|
echo " ic->tme_m68k_ireg_${sign}int32(ireg_dh) = (tme_${sign}int32_t) (res >> 32);"
|
|
echo " }"
|
|
fi
|
|
|
|
echo ""
|
|
echo " /* set the flags: */"
|
|
echo " flags = ic->tme_m68k_ireg_ccr & TME_M68K_FLAG_X;"
|
|
echo " if (((tme_int${large}_t) res) < 0) flags |= TME_M68K_FLAG_N;"
|
|
echo " if (res == 0) flags |= TME_M68K_FLAG_Z;"
|
|
if test $large = 64; then
|
|
if test $_sign = s; then
|
|
echo -n " if (res > 0x7fffffffL || res < ((0L - 0x7fffffffL) - 1L)"
|
|
else
|
|
echo -n " if (res > 0xffffffffUL"
|
|
fi
|
|
echo ") flags |= flag_v;"
|
|
fi
|
|
echo " ic->tme_m68k_ireg_ccr = flags;"
|
|
|
|
echo ""
|
|
echo " TME_M68K_INSN_OK;"
|
|
if test $large = 64; then
|
|
echo "#endif /* TME_HAVE_INT${large}_T */"
|
|
fi
|
|
echo "}"
|
|
|
|
# emit the divide function:
|
|
echo ""
|
|
echo "TME_M68K_INSN(tme_m68k_div${_sign}${_size})"
|
|
echo "{"
|
|
if test $large = 64; then
|
|
echo "#ifndef TME_HAVE_INT${large}_T"
|
|
echo " abort();"
|
|
echo "#else /* TME_HAVE_INT${large}_T */"
|
|
echo " int ireg_dr;"
|
|
fi
|
|
echo " int ireg_dq;"
|
|
echo " tme_${sign}int${large}_t dividend, quotient;"
|
|
echo " tme_${sign}int${small}_t divisor, remainder;"
|
|
echo " tme_uint8_t flags;"
|
|
|
|
echo ""
|
|
echo " /* get the register(s): */"
|
|
echo -n " ireg_dq = TME_M68K_IREG_D0 + "
|
|
if test $size = s; then
|
|
echo "TME_M68K_INSN_OP0(tme_uint32_t);"
|
|
else
|
|
echo "TME_FIELD_EXTRACTU(TME_M68K_INSN_SPECOP, 12, 3);"
|
|
echo " ireg_dr = TME_M68K_IREG_D0 + TME_FIELD_EXTRACTU(TME_M68K_INSN_SPECOP, 0, 3);"
|
|
fi
|
|
|
|
echo ""
|
|
echo " /* form the dividend and the divisor: */"
|
|
if test $large = 64; then
|
|
echo " if (TME_M68K_INSN_SPECOP & TME_BIT(10)) {"
|
|
echo " dividend = (tme_${sign}int${large}_t)"
|
|
echo " ((((tme_uint${large}_t) ic->tme_m68k_ireg_uint32(ireg_dr)) << 32)"
|
|
echo " | ic->tme_m68k_ireg_uint32(ireg_dq));"
|
|
echo " }"
|
|
echo " else"
|
|
echo -n " "
|
|
fi
|
|
echo " dividend = (tme_${sign}int${large}_t) ic->tme_m68k_ireg_${sign}int32(ireg_dq);"
|
|
echo " divisor = TME_M68K_INSN_OP1(tme_${sign}int${small}_t);"
|
|
echo " if (divisor == 0) {"
|
|
echo " ic->tme_m68k_ireg_pc_last = ic->tme_m68k_ireg_pc;"
|
|
echo " ic->tme_m68k_ireg_pc = ic->tme_m68k_ireg_pc_next;"
|
|
echo " TME_M68K_INSN_EXCEPTION(TME_M68K_EXCEPTION_INST(TME_M68K_VECTOR_DIV0));"
|
|
echo " }"
|
|
|
|
echo ""
|
|
echo " /* do the division: */"
|
|
echo " quotient = dividend / divisor;"
|
|
echo " remainder = dividend % divisor;"
|
|
|
|
echo ""
|
|
echo " /* set the flags and return the quotient and remainder: */"
|
|
echo " flags = ic->tme_m68k_ireg_ccr & TME_M68K_FLAG_X;"
|
|
echo -n " if ("
|
|
case "${small}${_sign}" in
|
|
16s) echo -n "quotient > 0x7fff || quotient < -32768" ;;
|
|
16u) echo -n "quotient > 0xffff" ;;
|
|
32s) echo -n "quotient > 0x7fffffffL || quotient < ((0L - 0x7fffffffL) - 1L)" ;;
|
|
32u) echo -n "quotient > 0xffffffffUL" ;;
|
|
esac
|
|
echo ") {"
|
|
echo " flags |= TME_M68K_FLAG_V;"
|
|
echo " }"
|
|
echo " else {"
|
|
echo " if (((tme_int${small}_t) quotient) < 0) flags |= TME_M68K_FLAG_N;"
|
|
echo " if (quotient == 0) flags |= TME_M68K_FLAG_Z;"
|
|
echo " ic->tme_m68k_ireg_${sign}int${small}(ireg_dq${reg_size_shift}) = (tme_${sign}int${small}_t) quotient;"
|
|
if test $small = 16; then
|
|
echo " ic->tme_m68k_ireg_${sign}int${small}((ireg_dq${reg_size_shift}) + 1) = remainder;"
|
|
else
|
|
echo " if (ireg_dr != ireg_dq) {"
|
|
echo " ic->tme_m68k_ireg_${sign}int${small}(ireg_dr) = remainder;"
|
|
echo " }"
|
|
fi
|
|
echo " }"
|
|
echo " ic->tme_m68k_ireg_ccr = flags;"
|
|
|
|
echo ""
|
|
echo " TME_M68K_INSN_OK;"
|
|
if test $large = 64; then
|
|
echo "#endif /* TME_HAVE_INT${large}_T */"
|
|
fi
|
|
echo "}"
|
|
|
|
done
|
|
done
|
|
|
|
# done:
|
|
exit 0
|