2014-05-03 23:27:38 +01:00
/*
* This file is part of the Micro Python project , http : //micropython.org/
*
* The MIT License ( MIT )
*
* Copyright ( c ) 2013 , 2014 Damien P . George
*
* Permission is hereby granted , free of charge , to any person obtaining a copy
* of this software and associated documentation files ( the " Software " ) , to deal
* in the Software without restriction , including without limitation the rights
* to use , copy , modify , merge , publish , distribute , sublicense , and / or sell
* copies of the Software , and to permit persons to whom the Software is
* furnished to do so , subject to the following conditions :
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software .
*
* THE SOFTWARE IS PROVIDED " AS IS " , WITHOUT WARRANTY OF ANY KIND , EXPRESS OR
* IMPLIED , INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY ,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT . IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM , DAMAGES OR OTHER
* LIABILITY , WHETHER IN AN ACTION OF CONTRACT , TORT OR OTHERWISE , ARISING FROM ,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE .
*/
2013-10-05 23:17:28 +01:00
# include <stdint.h>
# include <stdio.h>
# include <string.h>
2014-04-12 17:54:52 +01:00
# include <stdarg.h>
2013-10-05 23:17:28 +01:00
# include <assert.h>
2015-01-01 20:27:54 +00:00
# include "py/emit.h"
# include "py/asmthumb.h"
2013-10-05 23:17:28 +01:00
2013-10-12 16:53:13 +01:00
# if MICROPY_EMIT_INLINE_THUMB
2013-10-05 23:17:28 +01:00
2014-04-13 00:30:32 +01:00
typedef enum {
# define DEF_RULE(rule, comp, kind, ...) PN_##rule,
2015-01-01 20:27:54 +00:00
# include "py/grammar.h"
2014-04-13 00:30:32 +01:00
# undef DEF_RULE
PN_maximum_number_of ,
} pn_kind_t ;
2013-10-05 23:17:28 +01:00
struct _emit_inline_asm_t {
2014-04-12 17:54:52 +01:00
uint16_t pass ;
2013-10-05 23:17:28 +01:00
scope_t * scope ;
2015-02-13 01:00:51 +00:00
mp_obj_t * error_slot ;
2014-09-08 23:05:16 +01:00
mp_uint_t max_num_labels ;
2013-10-05 23:17:28 +01:00
qstr * label_lookup ;
asm_thumb_t * as ;
} ;
2015-02-13 01:00:51 +00:00
STATIC void emit_inline_thumb_error_msg ( emit_inline_asm_t * emit , const char * msg ) {
* emit - > error_slot = mp_obj_new_exception_msg ( & mp_type_SyntaxError , msg ) ;
}
STATIC void emit_inline_thumb_error_exc ( emit_inline_asm_t * emit , mp_obj_t exc ) {
* emit - > error_slot = exc ;
2014-04-12 17:54:52 +01:00
}
2014-09-08 23:05:16 +01:00
emit_inline_asm_t * emit_inline_thumb_new ( mp_uint_t max_num_labels ) {
2014-01-24 22:42:28 +00:00
emit_inline_asm_t * emit = m_new_obj ( emit_inline_asm_t ) ;
2013-10-05 23:17:28 +01:00
emit - > max_num_labels = max_num_labels ;
emit - > label_lookup = m_new ( qstr , max_num_labels ) ;
emit - > as = asm_thumb_new ( max_num_labels ) ;
return emit ;
}
2014-01-24 22:42:28 +00:00
void emit_inline_thumb_free ( emit_inline_asm_t * emit ) {
m_del ( qstr , emit - > label_lookup , emit - > max_num_labels ) ;
asm_thumb_free ( emit - > as , false ) ;
m_del_obj ( emit_inline_asm_t , emit ) ;
}
2015-02-13 01:00:51 +00:00
STATIC void emit_inline_thumb_start_pass ( emit_inline_asm_t * emit , pass_kind_t pass , scope_t * scope , mp_obj_t * error_slot ) {
2013-10-05 23:17:28 +01:00
emit - > pass = pass ;
emit - > scope = scope ;
2015-02-13 01:00:51 +00:00
emit - > error_slot = error_slot ;
2015-03-03 17:34:49 +00:00
if ( emit - > pass = = MP_PASS_CODE_SIZE ) {
memset ( emit - > label_lookup , 0 , emit - > max_num_labels * sizeof ( qstr ) ) ;
}
2014-05-07 17:24:22 +01:00
asm_thumb_start_pass ( emit - > as , pass = = MP_PASS_EMIT ? ASM_THUMB_PASS_EMIT : ASM_THUMB_PASS_COMPUTE ) ;
2013-10-05 23:17:28 +01:00
asm_thumb_entry ( emit - > as , 0 ) ;
}
2015-02-13 01:00:51 +00:00
STATIC void emit_inline_thumb_end_pass ( emit_inline_asm_t * emit ) {
2013-10-05 23:17:28 +01:00
asm_thumb_exit ( emit - > as ) ;
asm_thumb_end_pass ( emit - > as ) ;
2014-05-07 17:24:22 +01:00
if ( emit - > pass = = MP_PASS_EMIT ) {
2013-12-21 18:17:45 +00:00
void * f = asm_thumb_get_code ( emit - > as ) ;
2015-10-23 01:23:11 +01:00
mp_emit_glue_assign_native ( emit - > scope - > raw_code , MP_CODE_NATIVE_ASM , f , asm_thumb_get_code_size ( emit - > as ) , NULL , emit - > scope - > num_pos_args , 0 , 0 ) ;
2013-10-05 23:17:28 +01:00
}
}
2014-09-08 23:05:16 +01:00
STATIC mp_uint_t emit_inline_thumb_count_params ( emit_inline_asm_t * emit , mp_uint_t n_params , mp_parse_node_t * pn_params ) {
2013-10-06 00:14:13 +01:00
if ( n_params > 4 ) {
2015-02-13 01:00:51 +00:00
emit_inline_thumb_error_msg ( emit , " can only have up to 4 parameters to Thumb assembly " ) ;
2013-10-06 00:14:13 +01:00
return 0 ;
}
2014-09-08 23:05:16 +01:00
for ( mp_uint_t i = 0 ; i < n_params ; i + + ) {
2013-12-21 18:17:45 +00:00
if ( ! MP_PARSE_NODE_IS_ID ( pn_params [ i ] ) ) {
2015-02-13 01:00:51 +00:00
emit_inline_thumb_error_msg ( emit , " parameters must be registers in sequence r0 to r3 " ) ;
2013-10-06 00:14:13 +01:00
return 0 ;
}
2013-12-21 18:17:45 +00:00
const char * p = qstr_str ( MP_PARSE_NODE_LEAF_ARG ( pn_params [ i ] ) ) ;
2013-10-06 00:14:13 +01:00
if ( ! ( strlen ( p ) = = 2 & & p [ 0 ] = = ' r ' & & p [ 1 ] = = ' 0 ' + i ) ) {
2015-02-13 01:00:51 +00:00
emit_inline_thumb_error_msg ( emit , " parameters must be registers in sequence r0 to r3 " ) ;
2013-10-06 00:14:13 +01:00
return 0 ;
}
}
return n_params ;
}
2015-03-03 17:08:02 +00:00
STATIC bool emit_inline_thumb_label ( emit_inline_asm_t * emit , mp_uint_t label_num , qstr label_id ) {
2013-10-05 23:17:28 +01:00
assert ( label_num < emit - > max_num_labels ) ;
2015-03-03 17:08:02 +00:00
if ( emit - > pass = = MP_PASS_CODE_SIZE ) {
// check for duplicate label on first pass
2015-11-09 14:11:47 +00:00
for ( uint i = 0 ; i < emit - > max_num_labels ; i + + ) {
2015-03-03 17:08:02 +00:00
if ( emit - > label_lookup [ i ] = = label_id ) {
return false ;
}
}
}
2013-10-05 23:17:28 +01:00
emit - > label_lookup [ label_num ] = label_id ;
asm_thumb_label_assign ( emit - > as , label_num ) ;
2015-03-03 17:08:02 +00:00
return true ;
2013-10-05 23:17:28 +01:00
}
2014-09-08 23:05:16 +01:00
STATIC void emit_inline_thumb_align ( emit_inline_asm_t * emit , mp_uint_t align ) {
2014-04-21 13:33:15 +01:00
asm_thumb_align ( emit - > as , align ) ;
}
2014-09-08 23:05:16 +01:00
STATIC void emit_inline_thumb_data ( emit_inline_asm_t * emit , mp_uint_t bytesize , mp_uint_t val ) {
2014-04-21 13:33:15 +01:00
asm_thumb_data ( emit - > as , bytesize , val ) ;
}
2014-04-12 17:54:52 +01:00
typedef struct _reg_name_t { byte reg ; byte name [ 3 ] ; } reg_name_t ;
STATIC const reg_name_t reg_name_table [ ] = {
{ 0 , " r0 \0 " } ,
{ 1 , " r1 \0 " } ,
{ 2 , " r2 \0 " } ,
{ 3 , " r3 \0 " } ,
{ 4 , " r4 \0 " } ,
{ 5 , " r5 \0 " } ,
{ 6 , " r6 \0 " } ,
{ 7 , " r7 \0 " } ,
{ 8 , " r8 \0 " } ,
{ 9 , " r9 \0 " } ,
{ 10 , " r10 " } ,
{ 11 , " r11 " } ,
{ 12 , " r12 " } ,
{ 13 , " r13 " } ,
{ 14 , " r14 " } ,
{ 15 , " r15 " } ,
{ 10 , " sl \0 " } ,
{ 11 , " fp \0 " } ,
{ 13 , " sp \0 " } ,
{ 14 , " lr \0 " } ,
{ 15 , " pc \0 " } ,
} ;
2015-02-16 17:46:49 +00:00
// return empty string in case of error, so we can attempt to parse the string
// without a special check if it was in fact a string
STATIC const char * get_arg_str ( mp_parse_node_t pn ) {
2014-04-13 00:30:32 +01:00
if ( MP_PARSE_NODE_IS_ID ( pn ) ) {
2015-02-16 17:46:49 +00:00
qstr qst = MP_PARSE_NODE_LEAF_ARG ( pn ) ;
return qstr_str ( qst ) ;
} else {
return " " ;
}
}
STATIC mp_uint_t get_arg_reg ( emit_inline_asm_t * emit , const char * op , mp_parse_node_t pn , mp_uint_t max_reg ) {
const char * reg_str = get_arg_str ( pn ) ;
for ( mp_uint_t i = 0 ; i < MP_ARRAY_SIZE ( reg_name_table ) ; i + + ) {
const reg_name_t * r = & reg_name_table [ i ] ;
if ( reg_str [ 0 ] = = r - > name [ 0 ]
& & reg_str [ 1 ] = = r - > name [ 1 ]
& & reg_str [ 2 ] = = r - > name [ 2 ]
& & ( reg_str [ 2 ] = = ' \0 ' | | reg_str [ 3 ] = = ' \0 ' ) ) {
if ( r - > reg > max_reg ) {
emit_inline_thumb_error_exc ( emit ,
mp_obj_new_exception_msg_varg ( & mp_type_SyntaxError ,
" '%s' expects at most r%d " , op , max_reg ) ) ;
return 0 ;
} else {
return r - > reg ;
2014-04-12 17:54:52 +01:00
}
}
2013-10-05 23:17:28 +01:00
}
2015-02-16 17:46:49 +00:00
emit_inline_thumb_error_exc ( emit ,
mp_obj_new_exception_msg_varg ( & mp_type_SyntaxError ,
" '%s' expects a register " , op ) ) ;
2014-04-12 17:54:52 +01:00
return 0 ;
2013-10-05 23:17:28 +01:00
}
2015-04-14 13:14:57 +01:00
# if MICROPY_EMIT_INLINE_THUMB_FLOAT
STATIC mp_uint_t get_arg_vfpreg ( emit_inline_asm_t * emit , const char * op , mp_parse_node_t pn ) {
const char * reg_str = get_arg_str ( pn ) ;
if ( reg_str [ 0 ] = = ' s ' & & reg_str [ 1 ] ! = ' \0 ' ) {
mp_uint_t regno = 0 ;
for ( + + reg_str ; * reg_str ; + + reg_str ) {
mp_uint_t v = * reg_str ;
if ( ! ( ' 0 ' < = v & & v < = ' 9 ' ) ) {
goto malformed ;
}
regno = 10 * regno + v - ' 0 ' ;
}
if ( regno > 31 ) {
emit_inline_thumb_error_exc ( emit ,
mp_obj_new_exception_msg_varg ( & mp_type_SyntaxError ,
" '%s' expects at most r%d " , op , 31 ) ) ;
return 0 ;
} else {
return regno ;
}
}
malformed :
emit_inline_thumb_error_exc ( emit ,
mp_obj_new_exception_msg_varg ( & mp_type_SyntaxError ,
" '%s' expects an FPU register " , op ) ) ;
return 0 ;
}
# endif
2015-02-13 02:30:35 +00:00
STATIC mp_uint_t get_arg_reglist ( emit_inline_asm_t * emit , const char * op , mp_parse_node_t pn ) {
// a register list looks like {r0, r1, r2} and is parsed as a Python set
if ( ! MP_PARSE_NODE_IS_STRUCT_KIND ( pn , PN_atom_brace ) ) {
goto bad_arg ;
}
mp_parse_node_struct_t * pns = ( mp_parse_node_struct_t * ) pn ;
assert ( MP_PARSE_NODE_STRUCT_NUM_NODES ( pns ) = = 1 ) ; // should always be
pn = pns - > nodes [ 0 ] ;
mp_uint_t reglist = 0 ;
if ( MP_PARSE_NODE_IS_ID ( pn ) ) {
// set with one element
reglist | = 1 < < get_arg_reg ( emit , op , pn , 15 ) ;
} else if ( MP_PARSE_NODE_IS_STRUCT ( pn ) ) {
pns = ( mp_parse_node_struct_t * ) pn ;
if ( MP_PARSE_NODE_STRUCT_KIND ( pns ) = = PN_dictorsetmaker ) {
assert ( MP_PARSE_NODE_IS_STRUCT ( pns - > nodes [ 1 ] ) ) ; // should succeed
mp_parse_node_struct_t * pns1 = ( mp_parse_node_struct_t * ) pns - > nodes [ 1 ] ;
if ( MP_PARSE_NODE_STRUCT_KIND ( pns1 ) = = PN_dictorsetmaker_list ) {
// set with multiple elements
// get first element of set (we rely on get_arg_reg to catch syntax errors)
reglist | = 1 < < get_arg_reg ( emit , op , pns - > nodes [ 0 ] , 15 ) ;
// get tail elements (2nd, 3rd, ...)
mp_parse_node_t * nodes ;
int n = mp_parse_node_extract_list ( & pns1 - > nodes [ 0 ] , PN_dictorsetmaker_list2 , & nodes ) ;
// process rest of elements
for ( int i = 0 ; i < n ; i + + ) {
reglist | = 1 < < get_arg_reg ( emit , op , nodes [ i ] , 15 ) ;
}
} else {
goto bad_arg ;
}
} else {
goto bad_arg ;
}
} else {
goto bad_arg ;
}
return reglist ;
bad_arg :
emit_inline_thumb_error_exc ( emit , mp_obj_new_exception_msg_varg ( & mp_type_SyntaxError , " '%s' expects {r0, r1, ...} " , op ) ) ;
return 0 ;
}
2014-04-13 00:30:32 +01:00
STATIC int get_arg_i ( emit_inline_asm_t * emit , const char * op , mp_parse_node_t pn , int fit_mask ) {
if ( ! MP_PARSE_NODE_IS_SMALL_INT ( pn ) ) {
2015-02-13 01:00:51 +00:00
emit_inline_thumb_error_exc ( emit , mp_obj_new_exception_msg_varg ( & mp_type_SyntaxError , " '%s' expects an integer " , op ) ) ;
2013-10-05 23:17:28 +01:00
return 0 ;
}
2014-04-13 00:30:32 +01:00
int i = MP_PARSE_NODE_LEAF_SMALL_INT ( pn ) ;
2013-10-05 23:17:28 +01:00
if ( ( i & ( ~ fit_mask ) ) ! = 0 ) {
2015-02-13 01:00:51 +00:00
emit_inline_thumb_error_exc ( emit , mp_obj_new_exception_msg_varg ( & mp_type_SyntaxError , " '%s' integer 0x%x does not fit in mask 0x%x " , op , i , fit_mask ) ) ;
2013-10-05 23:17:28 +01:00
return 0 ;
}
return i ;
}
2014-04-13 00:30:32 +01:00
STATIC bool get_arg_addr ( emit_inline_asm_t * emit , const char * op , mp_parse_node_t pn , mp_parse_node_t * pn_base , mp_parse_node_t * pn_offset ) {
if ( ! MP_PARSE_NODE_IS_STRUCT_KIND ( pn , PN_atom_bracket ) ) {
goto bad_arg ;
}
mp_parse_node_struct_t * pns = ( mp_parse_node_struct_t * ) pn ;
if ( ! MP_PARSE_NODE_IS_STRUCT_KIND ( pns - > nodes [ 0 ] , PN_testlist_comp ) ) {
goto bad_arg ;
}
pns = ( mp_parse_node_struct_t * ) pns - > nodes [ 0 ] ;
if ( MP_PARSE_NODE_STRUCT_NUM_NODES ( pns ) ! = 2 ) {
goto bad_arg ;
}
* pn_base = pns - > nodes [ 0 ] ;
* pn_offset = pns - > nodes [ 1 ] ;
return true ;
bad_arg :
2015-02-13 01:00:51 +00:00
emit_inline_thumb_error_exc ( emit , mp_obj_new_exception_msg_varg ( & mp_type_SyntaxError , " '%s' expects an address of the form [a, b] " , op ) ) ;
2014-04-13 00:30:32 +01:00
return false ;
}
STATIC int get_arg_label ( emit_inline_asm_t * emit , const char * op , mp_parse_node_t pn ) {
if ( ! MP_PARSE_NODE_IS_ID ( pn ) ) {
2015-02-13 01:00:51 +00:00
emit_inline_thumb_error_exc ( emit , mp_obj_new_exception_msg_varg ( & mp_type_SyntaxError , " '%s' expects a label " , op ) ) ;
2013-10-05 23:17:28 +01:00
return 0 ;
}
2014-04-13 00:30:32 +01:00
qstr label_qstr = MP_PARSE_NODE_LEAF_ARG ( pn ) ;
2015-11-09 14:11:47 +00:00
for ( uint i = 0 ; i < emit - > max_num_labels ; i + + ) {
2013-10-05 23:17:28 +01:00
if ( emit - > label_lookup [ i ] = = label_qstr ) {
return i ;
}
}
2013-10-06 01:01:01 +01:00
// only need to have the labels on the last pass
2014-05-07 17:24:22 +01:00
if ( emit - > pass = = MP_PASS_EMIT ) {
2015-04-11 13:03:37 +01:00
emit_inline_thumb_error_exc ( emit , mp_obj_new_exception_msg_varg ( & mp_type_SyntaxError , " label '%q' not defined " , label_qstr ) ) ;
2013-10-06 01:01:01 +01:00
}
2013-10-05 23:17:28 +01:00
return 0 ;
}
2014-04-08 18:28:33 +01:00
typedef struct _cc_name_t { byte cc ; byte name [ 2 ] ; } cc_name_t ;
STATIC const cc_name_t cc_name_table [ ] = {
2014-09-29 16:25:04 +01:00
{ ASM_THUMB_CC_EQ , " eq " } ,
{ ASM_THUMB_CC_NE , " ne " } ,
{ ASM_THUMB_CC_CS , " cs " } ,
{ ASM_THUMB_CC_CC , " cc " } ,
{ ASM_THUMB_CC_MI , " mi " } ,
{ ASM_THUMB_CC_PL , " pl " } ,
{ ASM_THUMB_CC_VS , " vs " } ,
{ ASM_THUMB_CC_VC , " vc " } ,
{ ASM_THUMB_CC_HI , " hi " } ,
{ ASM_THUMB_CC_LS , " ls " } ,
{ ASM_THUMB_CC_GE , " ge " } ,
{ ASM_THUMB_CC_LT , " lt " } ,
{ ASM_THUMB_CC_GT , " gt " } ,
{ ASM_THUMB_CC_LE , " le " } ,
2014-04-08 18:28:33 +01:00
} ;
2015-02-24 16:10:58 +00:00
typedef struct _format_4_op_t { byte op ; char name [ 3 ] ; } format_4_op_t ;
# define X(x) (((x) >> 4) & 0xff) // only need 1 byte to distinguish these ops
STATIC const format_4_op_t format_4_op_table [ ] = {
{ X ( ASM_THUMB_FORMAT_4_EOR ) , " eor " } ,
{ X ( ASM_THUMB_FORMAT_4_LSL ) , " lsl " } ,
{ X ( ASM_THUMB_FORMAT_4_LSR ) , " lsr " } ,
{ X ( ASM_THUMB_FORMAT_4_ASR ) , " asr " } ,
{ X ( ASM_THUMB_FORMAT_4_ADC ) , " adc " } ,
{ X ( ASM_THUMB_FORMAT_4_SBC ) , " sbc " } ,
{ X ( ASM_THUMB_FORMAT_4_ROR ) , " ror " } ,
{ X ( ASM_THUMB_FORMAT_4_TST ) , " tst " } ,
{ X ( ASM_THUMB_FORMAT_4_NEG ) , " neg " } ,
{ X ( ASM_THUMB_FORMAT_4_CMP ) , " cmp " } ,
{ X ( ASM_THUMB_FORMAT_4_CMN ) , " cmn " } ,
{ X ( ASM_THUMB_FORMAT_4_ORR ) , " orr " } ,
{ X ( ASM_THUMB_FORMAT_4_MUL ) , " mul " } ,
{ X ( ASM_THUMB_FORMAT_4_BIC ) , " bic " } ,
{ X ( ASM_THUMB_FORMAT_4_MVN ) , " mvn " } ,
} ;
# undef X
typedef struct _format_9_10_op_t { uint16_t op ; char name [ 5 ] ; } format_9_10_op_t ;
# define X(x) (x)
STATIC const format_9_10_op_t format_9_10_op_table [ ] = {
{ X ( ASM_THUMB_FORMAT_9_LDR | ASM_THUMB_FORMAT_9_WORD_TRANSFER ) , " ldr " } ,
{ X ( ASM_THUMB_FORMAT_9_LDR | ASM_THUMB_FORMAT_9_BYTE_TRANSFER ) , " ldrb " } ,
{ X ( ASM_THUMB_FORMAT_10_LDRH ) , " ldrh " } ,
{ X ( ASM_THUMB_FORMAT_9_STR | ASM_THUMB_FORMAT_9_WORD_TRANSFER ) , " str " } ,
{ X ( ASM_THUMB_FORMAT_9_STR | ASM_THUMB_FORMAT_9_BYTE_TRANSFER ) , " strb " } ,
{ X ( ASM_THUMB_FORMAT_10_STRH ) , " strh " } ,
} ;
# undef X
2015-04-14 13:14:57 +01:00
# if MICROPY_EMIT_INLINE_THUMB_FLOAT
// actual opcodes are: 0xee00 | op.hi_nibble, 0x0a00 | op.lo_nibble
typedef struct _format_vfp_op_t { byte op ; char name [ 3 ] ; } format_vfp_op_t ;
STATIC const format_vfp_op_t format_vfp_op_table [ ] = {
{ 0x30 , " add " } ,
{ 0x34 , " sub " } ,
{ 0x20 , " mul " } ,
{ 0x80 , " div " } ,
} ;
# endif
2015-10-16 22:08:57 +01:00
// shorthand alias for whether we allow ARMv7-M instructions
# define ARMV7M MICROPY_EMIT_INLINE_THUMB_ARMV7M
2014-09-08 23:05:16 +01:00
STATIC void emit_inline_thumb_op ( emit_inline_asm_t * emit , qstr op , mp_uint_t n_args , mp_parse_node_t * pn_args ) {
2013-10-05 23:17:28 +01:00
// TODO perhaps make two tables:
2013-10-06 00:36:05 +01:00
// one_args =
// "b", LAB, asm_thumb_b_n,
// "bgt", LAB, asm_thumb_bgt_n,
2013-10-05 23:17:28 +01:00
// two_args =
// "movs", RLO, I8, asm_thumb_movs_reg_i8
// "movw", REG, REG, asm_thumb_movw_reg_i16
// three_args =
// "subs", RLO, RLO, I3, asm_thumb_subs_reg_reg_i3
2015-04-11 21:53:39 +01:00
mp_uint_t op_len ;
const char * op_str = ( const char * ) qstr_data ( op , & op_len ) ;
2014-04-08 18:28:33 +01:00
2015-04-14 13:14:57 +01:00
# if MICROPY_EMIT_INLINE_THUMB_FLOAT
if ( op_str [ 0 ] = = ' v ' ) {
// floating point operations
if ( n_args = = 2 ) {
mp_uint_t op_code = 0x0ac0 , op_code_hi ;
if ( strcmp ( op_str , " vcmp " ) = = 0 ) {
op_code_hi = 0xeeb4 ;
op_vfp_twoargs : ;
mp_uint_t vd = get_arg_vfpreg ( emit , op_str , pn_args [ 0 ] ) ;
mp_uint_t vm = get_arg_vfpreg ( emit , op_str , pn_args [ 1 ] ) ;
asm_thumb_op32 ( emit - > as ,
op_code_hi | ( ( vd & 1 ) < < 6 ) ,
op_code | ( ( vd & 0x1e ) < < 11 ) | ( ( vm & 1 ) < < 5 ) | ( vm & 0x1e ) > > 1 ) ;
} else if ( strcmp ( op_str , " vsqrt " ) = = 0 ) {
op_code_hi = 0xeeb1 ;
goto op_vfp_twoargs ;
} else if ( strcmp ( op_str , " vneg " ) = = 0 ) {
op_code_hi = 0xeeb1 ;
op_code = 0x0a40 ;
goto op_vfp_twoargs ;
} else if ( strcmp ( op_str , " vcvt_f32_s32 " ) = = 0 ) {
op_code_hi = 0xeeb8 ; // int to float
goto op_vfp_twoargs ;
} else if ( strcmp ( op_str , " vcvt_s32_f32 " ) = = 0 ) {
op_code_hi = 0xeebd ; // float to int
goto op_vfp_twoargs ;
} else if ( strcmp ( op_str , " vmrs " ) = = 0 ) {
mp_uint_t reg_dest ;
const char * reg_str0 = get_arg_str ( pn_args [ 0 ] ) ;
if ( strcmp ( reg_str0 , " APSR_nzcv " ) = = 0 ) {
reg_dest = 15 ;
} else {
reg_dest = get_arg_reg ( emit , op_str , pn_args [ 0 ] , 15 ) ;
}
const char * reg_str1 = get_arg_str ( pn_args [ 1 ] ) ;
if ( strcmp ( reg_str1 , " FPSCR " ) = = 0 ) {
// FP status to ARM reg
asm_thumb_op32 ( emit - > as , 0xeef1 , 0x0a10 | ( reg_dest < < 12 ) ) ;
} else {
goto unknown_op ;
}
} else if ( strcmp ( op_str , " vmov " ) = = 0 ) {
op_code_hi = 0xee00 ;
mp_uint_t r_arm , vm ;
const char * reg_str = get_arg_str ( pn_args [ 0 ] ) ;
if ( reg_str [ 0 ] = = ' r ' ) {
r_arm = get_arg_reg ( emit , op_str , pn_args [ 0 ] , 15 ) ;
vm = get_arg_vfpreg ( emit , op_str , pn_args [ 1 ] ) ;
op_code_hi | = 0x10 ;
} else {
vm = get_arg_vfpreg ( emit , op_str , pn_args [ 0 ] ) ;
r_arm = get_arg_reg ( emit , op_str , pn_args [ 1 ] , 15 ) ;
}
asm_thumb_op32 ( emit - > as ,
op_code_hi | ( ( vm & 0x1e ) > > 1 ) ,
0x0a10 | ( r_arm < < 12 ) | ( ( vm & 1 ) < < 7 ) ) ;
} else if ( strcmp ( op_str , " vldr " ) = = 0 ) {
op_code_hi = 0xed90 ;
op_vldr_vstr : ;
mp_uint_t vd = get_arg_vfpreg ( emit , op_str , pn_args [ 0 ] ) ;
mp_parse_node_t pn_base , pn_offset ;
if ( get_arg_addr ( emit , op_str , pn_args [ 1 ] , & pn_base , & pn_offset ) ) {
mp_uint_t rlo_base = get_arg_reg ( emit , op_str , pn_base , 7 ) ;
mp_uint_t i8 ;
2015-10-31 09:38:22 +00:00
i8 = get_arg_i ( emit , op_str , pn_offset , 0x3fc ) > > 2 ;
2015-04-14 13:14:57 +01:00
asm_thumb_op32 ( emit - > as ,
op_code_hi | rlo_base | ( ( vd & 1 ) < < 6 ) ,
0x0a00 | ( ( vd & 0x1e ) < < 11 ) | i8 ) ;
}
} else if ( strcmp ( op_str , " vstr " ) = = 0 ) {
op_code_hi = 0xed80 ;
goto op_vldr_vstr ;
} else {
goto unknown_op ;
}
} else if ( n_args = = 3 ) {
// search table for arith ops
for ( mp_uint_t i = 0 ; i < MP_ARRAY_SIZE ( format_vfp_op_table ) ; i + + ) {
if ( strncmp ( op_str + 1 , format_vfp_op_table [ i ] . name , 3 ) = = 0 & & op_str [ 4 ] = = ' \0 ' ) {
mp_uint_t op_code_hi = 0xee00 | ( format_vfp_op_table [ i ] . op & 0xf0 ) ;
mp_uint_t op_code = 0x0a00 | ( ( format_vfp_op_table [ i ] . op & 0x0f ) < < 4 ) ;
mp_uint_t vd = get_arg_vfpreg ( emit , op_str , pn_args [ 0 ] ) ;
mp_uint_t vn = get_arg_vfpreg ( emit , op_str , pn_args [ 1 ] ) ;
mp_uint_t vm = get_arg_vfpreg ( emit , op_str , pn_args [ 2 ] ) ;
asm_thumb_op32 ( emit - > as ,
op_code_hi | ( ( vd & 1 ) < < 6 ) | ( vn > > 1 ) ,
op_code | ( vm > > 1 ) | ( ( vm & 1 ) < < 5 ) | ( ( vd & 0x1e ) < < 11 ) | ( ( vn & 1 ) < < 7 ) ) ;
return ;
}
}
goto unknown_op ;
} else {
goto unknown_op ;
}
} else
# endif
2014-04-08 18:28:33 +01:00
if ( n_args = = 0 ) {
2014-04-18 16:56:54 +01:00
if ( strcmp ( op_str , " nop " ) = = 0 ) {
asm_thumb_op16 ( emit - > as , ASM_THUMB_OP_NOP ) ;
} else if ( strcmp ( op_str , " wfi " ) = = 0 ) {
asm_thumb_op16 ( emit - > as , ASM_THUMB_OP_WFI ) ;
2014-04-08 18:28:33 +01:00
} else {
goto unknown_op ;
2013-10-05 23:17:28 +01:00
}
2014-04-08 18:28:33 +01:00
} else if ( n_args = = 1 ) {
if ( strcmp ( op_str , " b " ) = = 0 ) {
2014-04-13 00:30:32 +01:00
int label_num = get_arg_label ( emit , op_str , pn_args [ 0 ] ) ;
2015-02-25 15:45:55 +00:00
if ( ! asm_thumb_b_n_label ( emit - > as , label_num ) ) {
goto branch_not_in_range ;
}
2015-02-16 18:17:07 +00:00
} else if ( strcmp ( op_str , " bl " ) = = 0 ) {
int label_num = get_arg_label ( emit , op_str , pn_args [ 0 ] ) ;
2015-02-25 15:45:55 +00:00
if ( ! asm_thumb_bl_label ( emit - > as , label_num ) ) {
goto branch_not_in_range ;
}
2015-02-16 18:17:07 +00:00
} else if ( strcmp ( op_str , " bx " ) = = 0 ) {
mp_uint_t r = get_arg_reg ( emit , op_str , pn_args [ 0 ] , 15 ) ;
asm_thumb_op16 ( emit - > as , 0x4700 | ( r < < 3 ) ) ;
2015-03-02 14:29:52 +00:00
} else if ( op_str [ 0 ] = = ' b ' & & ( op_len = = 3
| | ( op_len = = 5 & & op_str [ 3 ] = = ' _ '
2015-10-16 22:08:57 +01:00
& & ( op_str [ 4 ] = = ' n ' | | ( ARMV7M & & op_str [ 4 ] = = ' w ' ) ) ) ) ) {
2014-09-08 23:05:16 +01:00
mp_uint_t cc = - 1 ;
for ( mp_uint_t i = 0 ; i < MP_ARRAY_SIZE ( cc_name_table ) ; i + + ) {
2014-04-08 18:28:33 +01:00
if ( op_str [ 1 ] = = cc_name_table [ i ] . name [ 0 ] & & op_str [ 2 ] = = cc_name_table [ i ] . name [ 1 ] ) {
cc = cc_name_table [ i ] . cc ;
}
}
2015-11-09 14:11:47 +00:00
if ( cc = = ( mp_uint_t ) - 1 ) {
2014-04-08 18:28:33 +01:00
goto unknown_op ;
}
2014-04-13 00:30:32 +01:00
int label_num = get_arg_label ( emit , op_str , pn_args [ 0 ] ) ;
2015-03-02 14:29:52 +00:00
if ( ! asm_thumb_bcc_nw_label ( emit - > as , cc , label_num , op_len = = 5 & & op_str [ 4 ] = = ' w ' ) ) {
2015-02-25 15:45:55 +00:00
goto branch_not_in_range ;
}
2015-10-16 22:08:57 +01:00
} else if ( ARMV7M & & op_str [ 0 ] = = ' i ' & & op_str [ 1 ] = = ' t ' ) {
2015-02-16 17:46:49 +00:00
const char * arg_str = get_arg_str ( pn_args [ 0 ] ) ;
mp_uint_t cc = - 1 ;
for ( mp_uint_t i = 0 ; i < MP_ARRAY_SIZE ( cc_name_table ) ; i + + ) {
if ( arg_str [ 0 ] = = cc_name_table [ i ] . name [ 0 ]
& & arg_str [ 1 ] = = cc_name_table [ i ] . name [ 1 ]
& & arg_str [ 2 ] = = ' \0 ' ) {
cc = cc_name_table [ i ] . cc ;
break ;
}
}
2015-11-09 14:11:47 +00:00
if ( cc = = ( mp_uint_t ) - 1 ) {
2015-02-16 17:46:49 +00:00
goto unknown_op ;
}
const char * os = op_str + 2 ;
while ( * os ! = ' \0 ' ) {
os + + ;
}
if ( os > op_str + 5 ) {
goto unknown_op ;
}
mp_uint_t it_mask = 8 ;
while ( - - os > = op_str + 2 ) {
it_mask > > = 1 ;
if ( * os = = ' t ' ) {
it_mask | = ( cc & 1 ) < < 3 ;
} else if ( * os = = ' e ' ) {
it_mask | = ( ( ~ cc ) & 1 ) < < 3 ;
} else {
goto unknown_op ;
}
}
asm_thumb_it_cc ( emit - > as , cc , it_mask ) ;
2015-02-13 02:30:35 +00:00
} else if ( strcmp ( op_str , " cpsid " ) = = 0 ) {
2014-04-18 16:56:54 +01:00
// TODO check pn_args[0] == i
asm_thumb_op16 ( emit - > as , ASM_THUMB_OP_CPSID_I ) ;
2015-02-13 02:30:35 +00:00
} else if ( strcmp ( op_str , " cpsie " ) = = 0 ) {
2014-04-18 16:56:54 +01:00
// TODO check pn_args[0] == i
asm_thumb_op16 ( emit - > as , ASM_THUMB_OP_CPSIE_I ) ;
2015-02-13 02:30:35 +00:00
} else if ( strcmp ( op_str , " push " ) = = 0 ) {
mp_uint_t reglist = get_arg_reglist ( emit , op_str , pn_args [ 0 ] ) ;
if ( ( reglist & 0xff00 ) = = 0 ) {
asm_thumb_op16 ( emit - > as , 0xb400 | reglist ) ;
} else {
2015-10-16 22:08:57 +01:00
if ( ! ARMV7M ) {
goto unknown_op ;
}
2015-02-13 02:30:35 +00:00
asm_thumb_op32 ( emit - > as , 0xe92d , reglist ) ;
}
} else if ( strcmp ( op_str , " pop " ) = = 0 ) {
mp_uint_t reglist = get_arg_reglist ( emit , op_str , pn_args [ 0 ] ) ;
if ( ( reglist & 0xff00 ) = = 0 ) {
asm_thumb_op16 ( emit - > as , 0xbc00 | reglist ) ;
} else {
2015-10-16 22:08:57 +01:00
if ( ! ARMV7M ) {
goto unknown_op ;
}
2015-02-13 02:30:35 +00:00
asm_thumb_op32 ( emit - > as , 0xe8bd , reglist ) ;
}
2014-04-08 18:28:33 +01:00
} else {
goto unknown_op ;
2013-10-05 23:17:28 +01:00
}
2014-04-08 18:28:33 +01:00
} else if ( n_args = = 2 ) {
2014-04-13 00:30:32 +01:00
if ( MP_PARSE_NODE_IS_ID ( pn_args [ 1 ] ) ) {
// second arg is a register (or should be)
2015-02-13 11:06:23 +00:00
mp_uint_t op_code , op_code_hi ;
2014-04-13 00:30:32 +01:00
if ( strcmp ( op_str , " mov " ) = = 0 ) {
2014-09-08 23:05:16 +01:00
mp_uint_t reg_dest = get_arg_reg ( emit , op_str , pn_args [ 0 ] , 15 ) ;
mp_uint_t reg_src = get_arg_reg ( emit , op_str , pn_args [ 1 ] , 15 ) ;
2014-04-13 00:30:32 +01:00
asm_thumb_mov_reg_reg ( emit - > as , reg_dest , reg_src ) ;
2015-10-16 22:08:57 +01:00
} else if ( ARMV7M & & strcmp ( op_str , " clz " ) = = 0 ) {
2015-02-13 11:06:23 +00:00
op_code_hi = 0xfab0 ;
op_code = 0xf080 ;
mp_uint_t rd , rm ;
op_clz_rbit :
rd = get_arg_reg ( emit , op_str , pn_args [ 0 ] , 15 ) ;
rm = get_arg_reg ( emit , op_str , pn_args [ 1 ] , 15 ) ;
asm_thumb_op32 ( emit - > as , op_code_hi | rm , op_code | ( rd < < 8 ) | rm ) ;
2015-10-16 22:08:57 +01:00
} else if ( ARMV7M & & strcmp ( op_str , " rbit " ) = = 0 ) {
2015-02-13 11:06:23 +00:00
op_code_hi = 0xfa90 ;
op_code = 0xf0a0 ;
goto op_clz_rbit ;
2014-04-13 00:30:32 +01:00
} else {
2015-02-24 16:10:58 +00:00
if ( strcmp ( op_str , " and_ " ) = = 0 ) {
op_code = ASM_THUMB_FORMAT_4_AND ;
mp_uint_t reg_dest , reg_src ;
op_format_4 :
reg_dest = get_arg_reg ( emit , op_str , pn_args [ 0 ] , 7 ) ;
reg_src = get_arg_reg ( emit , op_str , pn_args [ 1 ] , 7 ) ;
asm_thumb_format_4 ( emit - > as , op_code , reg_dest , reg_src ) ;
2015-02-24 22:43:01 +00:00
return ;
2015-02-24 16:10:58 +00:00
}
2015-02-24 16:35:37 +00:00
// search table for ALU ops
2015-02-24 16:10:58 +00:00
for ( mp_uint_t i = 0 ; i < MP_ARRAY_SIZE ( format_4_op_table ) ; i + + ) {
if ( strncmp ( op_str , format_4_op_table [ i ] . name , 3 ) = = 0 & & op_str [ 3 ] = = ' \0 ' ) {
op_code = 0x4000 | ( format_4_op_table [ i ] . op < < 4 ) ;
goto op_format_4 ;
}
}
2014-04-13 00:30:32 +01:00
goto unknown_op ;
}
2014-04-08 18:28:33 +01:00
} else {
2014-04-13 00:30:32 +01:00
// second arg is not a register
2014-09-08 23:05:16 +01:00
mp_uint_t op_code ;
2014-04-13 00:30:32 +01:00
if ( strcmp ( op_str , " mov " ) = = 0 ) {
op_code = ASM_THUMB_FORMAT_3_MOV ;
2014-09-08 23:05:16 +01:00
mp_uint_t rlo_dest , i8_src ;
2014-04-13 00:30:32 +01:00
op_format_3 :
rlo_dest = get_arg_reg ( emit , op_str , pn_args [ 0 ] , 7 ) ;
i8_src = get_arg_i ( emit , op_str , pn_args [ 1 ] , 0xff ) ;
asm_thumb_format_3 ( emit - > as , op_code , rlo_dest , i8_src ) ;
} else if ( strcmp ( op_str , " cmp " ) = = 0 ) {
op_code = ASM_THUMB_FORMAT_3_CMP ;
goto op_format_3 ;
} else if ( strcmp ( op_str , " add " ) = = 0 ) {
op_code = ASM_THUMB_FORMAT_3_ADD ;
goto op_format_3 ;
} else if ( strcmp ( op_str , " sub " ) = = 0 ) {
op_code = ASM_THUMB_FORMAT_3_SUB ;
goto op_format_3 ;
2015-10-16 22:08:57 +01:00
} else if ( ARMV7M & & strcmp ( op_str , " movw " ) = = 0 ) {
2015-02-24 16:35:37 +00:00
op_code = ASM_THUMB_OP_MOVW ;
mp_uint_t reg_dest ;
op_movw_movt :
reg_dest = get_arg_reg ( emit , op_str , pn_args [ 0 ] , 15 ) ;
2014-04-13 00:30:32 +01:00
int i_src = get_arg_i ( emit , op_str , pn_args [ 1 ] , 0xffff ) ;
2015-02-24 16:35:37 +00:00
asm_thumb_mov_reg_i16 ( emit - > as , op_code , reg_dest , i_src ) ;
2015-10-16 22:08:57 +01:00
} else if ( ARMV7M & & strcmp ( op_str , " movt " ) = = 0 ) {
2015-02-24 16:35:37 +00:00
op_code = ASM_THUMB_OP_MOVT ;
goto op_movw_movt ;
2015-10-16 22:08:57 +01:00
} else if ( ARMV7M & & strcmp ( op_str , " movwt " ) = = 0 ) {
2014-04-13 00:30:32 +01:00
// this is a convenience instruction
// we clear the MSB since it might be set from extracting the small int value
2014-09-08 23:05:16 +01:00
mp_uint_t reg_dest = get_arg_reg ( emit , op_str , pn_args [ 0 ] , 15 ) ;
2014-04-13 00:30:32 +01:00
int i_src = get_arg_i ( emit , op_str , pn_args [ 1 ] , 0xffffffff ) ;
2015-02-24 16:32:52 +00:00
asm_thumb_mov_reg_i16 ( emit - > as , ASM_THUMB_OP_MOVW , reg_dest , i_src & 0xffff ) ;
asm_thumb_mov_reg_i16 ( emit - > as , ASM_THUMB_OP_MOVT , reg_dest , ( i_src > > 16 ) & 0x7fff ) ;
2015-10-16 22:08:57 +01:00
} else if ( ARMV7M & & strcmp ( op_str , " ldrex " ) = = 0 ) {
2015-02-12 22:52:42 +00:00
mp_uint_t r_dest = get_arg_reg ( emit , op_str , pn_args [ 0 ] , 15 ) ;
mp_parse_node_t pn_base , pn_offset ;
if ( get_arg_addr ( emit , op_str , pn_args [ 1 ] , & pn_base , & pn_offset ) ) {
mp_uint_t r_base = get_arg_reg ( emit , op_str , pn_base , 15 ) ;
mp_uint_t i8 = get_arg_i ( emit , op_str , pn_offset , 0xff ) > > 2 ;
asm_thumb_op32 ( emit - > as , 0xe850 | r_base , 0x0f00 | ( r_dest < < 12 ) | i8 ) ;
}
2014-04-13 00:30:32 +01:00
} else {
2015-02-24 16:10:58 +00:00
// search table for ldr/str instructions
for ( mp_uint_t i = 0 ; i < MP_ARRAY_SIZE ( format_9_10_op_table ) ; i + + ) {
if ( strcmp ( op_str , format_9_10_op_table [ i ] . name ) = = 0 ) {
op_code = format_9_10_op_table [ i ] . op ;
mp_parse_node_t pn_base , pn_offset ;
mp_uint_t rlo_dest = get_arg_reg ( emit , op_str , pn_args [ 0 ] , 7 ) ;
if ( get_arg_addr ( emit , op_str , pn_args [ 1 ] , & pn_base , & pn_offset ) ) {
mp_uint_t rlo_base = get_arg_reg ( emit , op_str , pn_base , 7 ) ;
mp_uint_t i5 ;
if ( op_code & ASM_THUMB_FORMAT_9_BYTE_TRANSFER ) {
i5 = get_arg_i ( emit , op_str , pn_offset , 0x1f ) ;
} else if ( op_code & ASM_THUMB_FORMAT_10_STRH ) { // also catches LDRH
i5 = get_arg_i ( emit , op_str , pn_offset , 0x3e ) > > 1 ;
} else {
i5 = get_arg_i ( emit , op_str , pn_offset , 0x7c ) > > 2 ;
}
asm_thumb_format_9_10 ( emit - > as , op_code , rlo_dest , rlo_base , i5 ) ;
return ;
}
break ;
}
}
2014-04-13 00:30:32 +01:00
goto unknown_op ;
}
2013-10-05 23:17:28 +01:00
}
2014-04-08 18:28:33 +01:00
} else if ( n_args = = 3 ) {
2014-09-08 23:05:16 +01:00
mp_uint_t op_code ;
2015-10-19 14:26:19 +01:00
if ( strcmp ( op_str , " lsl " ) = = 0 ) {
op_code = ASM_THUMB_FORMAT_1_LSL ;
mp_uint_t rlo_dest , rlo_src , i5 ;
op_format_1 :
rlo_dest = get_arg_reg ( emit , op_str , pn_args [ 0 ] , 7 ) ;
rlo_src = get_arg_reg ( emit , op_str , pn_args [ 1 ] , 7 ) ;
i5 = get_arg_i ( emit , op_str , pn_args [ 2 ] , 0x1f ) ;
asm_thumb_format_1 ( emit - > as , op_code , rlo_dest , rlo_src , i5 ) ;
} else if ( strcmp ( op_str , " lsr " ) = = 0 ) {
op_code = ASM_THUMB_FORMAT_1_LSR ;
goto op_format_1 ;
} else if ( strcmp ( op_str , " asr " ) = = 0 ) {
op_code = ASM_THUMB_FORMAT_1_ASR ;
goto op_format_1 ;
} else if ( strcmp ( op_str , " add " ) = = 0 ) {
2014-04-13 00:30:32 +01:00
op_code = ASM_THUMB_FORMAT_2_ADD ;
2014-09-08 23:05:16 +01:00
mp_uint_t rlo_dest , rlo_src ;
2014-04-13 00:30:32 +01:00
op_format_2 :
rlo_dest = get_arg_reg ( emit , op_str , pn_args [ 0 ] , 7 ) ;
rlo_src = get_arg_reg ( emit , op_str , pn_args [ 1 ] , 7 ) ;
int src_b ;
if ( MP_PARSE_NODE_IS_ID ( pn_args [ 2 ] ) ) {
op_code | = ASM_THUMB_FORMAT_2_REG_OPERAND ;
src_b = get_arg_reg ( emit , op_str , pn_args [ 2 ] , 7 ) ;
} else {
op_code | = ASM_THUMB_FORMAT_2_IMM_OPERAND ;
src_b = get_arg_i ( emit , op_str , pn_args [ 2 ] , 0x7 ) ;
}
asm_thumb_format_2 ( emit - > as , op_code , rlo_dest , rlo_src , src_b ) ;
2015-10-16 22:08:57 +01:00
} else if ( ARMV7M & & strcmp ( op_str , " sdiv " ) = = 0 ) {
2015-02-13 10:43:05 +00:00
op_code = 0xfb90 ; // sdiv high part
mp_uint_t rd , rn , rm ;
op_sdiv_udiv :
rd = get_arg_reg ( emit , op_str , pn_args [ 0 ] , 15 ) ;
rn = get_arg_reg ( emit , op_str , pn_args [ 1 ] , 15 ) ;
rm = get_arg_reg ( emit , op_str , pn_args [ 2 ] , 15 ) ;
asm_thumb_op32 ( emit - > as , op_code | rn , 0xf0f0 | ( rd < < 8 ) | rm ) ;
2015-10-16 22:08:57 +01:00
} else if ( ARMV7M & & strcmp ( op_str , " udiv " ) = = 0 ) {
2015-02-13 10:43:05 +00:00
op_code = 0xfbb0 ; // udiv high part
goto op_sdiv_udiv ;
2014-04-13 00:30:32 +01:00
} else if ( strcmp ( op_str , " sub " ) = = 0 ) {
op_code = ASM_THUMB_FORMAT_2_SUB ;
goto op_format_2 ;
2015-10-16 22:08:57 +01:00
} else if ( ARMV7M & & strcmp ( op_str , " strex " ) = = 0 ) {
2015-02-12 22:52:42 +00:00
mp_uint_t r_dest = get_arg_reg ( emit , op_str , pn_args [ 0 ] , 15 ) ;
mp_uint_t r_src = get_arg_reg ( emit , op_str , pn_args [ 1 ] , 15 ) ;
mp_parse_node_t pn_base , pn_offset ;
if ( get_arg_addr ( emit , op_str , pn_args [ 2 ] , & pn_base , & pn_offset ) ) {
mp_uint_t r_base = get_arg_reg ( emit , op_str , pn_base , 15 ) ;
mp_uint_t i8 = get_arg_i ( emit , op_str , pn_offset , 0xff ) > > 2 ;
asm_thumb_op32 ( emit - > as , 0xe840 | r_base , ( r_src < < 12 ) | ( r_dest < < 8 ) | i8 ) ;
}
2014-04-08 18:28:33 +01:00
} else {
goto unknown_op ;
2013-10-05 23:17:28 +01:00
}
} else {
2014-04-08 18:28:33 +01:00
goto unknown_op ;
2013-10-05 23:17:28 +01:00
}
2014-04-08 18:28:33 +01:00
return ;
unknown_op :
2015-02-13 01:00:51 +00:00
emit_inline_thumb_error_exc ( emit , mp_obj_new_exception_msg_varg ( & mp_type_SyntaxError , " unsupported Thumb instruction '%s' with %d arguments " , op_str , n_args ) ) ;
2015-02-25 15:45:55 +00:00
return ;
branch_not_in_range :
emit_inline_thumb_error_msg ( emit , " branch not in range " ) ;
return ;
2013-10-05 23:17:28 +01:00
}
const emit_inline_asm_method_table_t emit_inline_thumb_method_table = {
emit_inline_thumb_start_pass ,
emit_inline_thumb_end_pass ,
2013-10-06 00:14:13 +01:00
emit_inline_thumb_count_params ,
2013-10-05 23:17:28 +01:00
emit_inline_thumb_label ,
2014-04-21 13:33:15 +01:00
emit_inline_thumb_align ,
emit_inline_thumb_data ,
2013-10-05 23:17:28 +01:00
emit_inline_thumb_op ,
} ;
2013-10-12 16:53:13 +01:00
# endif // MICROPY_EMIT_INLINE_THUMB