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 ) ;
memset ( emit - > label_lookup , 0 , emit - > max_num_labels * sizeof ( qstr ) ) ;
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 ;
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 ) ;
2014-08-15 16:45:41 +01:00
mp_emit_glue_assign_native ( emit - > scope - > raw_code , MP_CODE_NATIVE_ASM , f , asm_thumb_get_code_size ( emit - > as ) , emit - > scope - > num_pos_args , 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 ;
}
2014-09-08 23:05:16 +01:00
STATIC void 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 ) ;
emit - > label_lookup [ label_num ] = label_id ;
asm_thumb_label_assign ( emit - > as , label_num ) ;
}
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 " } ,
} ;
2014-09-08 23:05:16 +01:00
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 ) {
2014-04-13 00:30:32 +01:00
if ( MP_PARSE_NODE_IS_ID ( pn ) ) {
qstr reg_qstr = MP_PARSE_NODE_LEAF_ARG ( pn ) ;
2014-04-12 17:54:52 +01:00
const char * reg_str = qstr_str ( reg_qstr ) ;
2014-09-08 23:05:16 +01:00
for ( mp_uint_t i = 0 ; i < MP_ARRAY_SIZE ( reg_name_table ) ; i + + ) {
2014-04-12 17:54:52 +01:00
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 ) {
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 at most r%d " , op , max_reg ) ) ;
2014-04-12 17:54:52 +01:00
return 0 ;
} else {
return r - > reg ;
}
}
}
2013-10-05 23:17:28 +01:00
}
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 register " , op ) ) ;
2014-04-12 17:54:52 +01:00
return 0 ;
2013-10-05 23:17:28 +01:00
}
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 ) ;
2013-10-05 23:17:28 +01:00
for ( int i = 0 ; i < emit - > max_num_labels ; i + + ) {
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-02-13 01:00:51 +00:00
emit_inline_thumb_error_exc ( emit , mp_obj_new_exception_msg_varg ( & mp_type_SyntaxError , " label '%s' not defined " , qstr_str ( 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
} ;
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
2014-04-08 18:28:33 +01:00
const char * op_str = qstr_str ( op ) ;
2014-09-08 23:05:16 +01:00
mp_uint_t op_len = strlen ( op_str ) ;
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 ) ;
} else if ( strcmp ( op_str , " ite.ge " ) = = 0 ) { // TODO correct name for this op?
2014-09-29 10:05:32 +01:00
asm_thumb_op16 ( emit - > as , ASM_THUMB_OP_ITE_GE ) ;
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 ] ) ;
2014-04-08 18:28:33 +01:00
// TODO check that this succeeded, ie branch was within range
asm_thumb_b_n ( emit - > as , label_num ) ;
} else if ( op_str [ 0 ] = = ' b ' & & op_len = = 3 ) {
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 ;
}
}
if ( cc = = - 1 ) {
goto unknown_op ;
}
2014-04-13 00:30:32 +01:00
int label_num = get_arg_label ( emit , op_str , pn_args [ 0 ] ) ;
2014-04-08 18:28:33 +01:00
// TODO check that this succeeded, ie branch was within range
asm_thumb_bcc_n ( emit - > as , cc , label_num ) ;
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 {
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 {
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)
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 ) {
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 ) ;
2014-07-17 12:37:56 +01:00
} else if ( strcmp ( op_str , " and_ " ) = = 0 ) {
2014-04-13 00:30:32 +01:00
op_code = ASM_THUMB_FORMAT_4_AND ;
2014-09-08 23:05:16 +01:00
mp_uint_t reg_dest , reg_src ;
2014-04-13 00:30:32 +01:00
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 ) ;
// TODO probably uses less ROM if these ops are in a lookup table
} else if ( strcmp ( op_str , " eor " ) = = 0 ) { op_code = ASM_THUMB_FORMAT_4_EOR ; goto op_format_4 ;
} else if ( strcmp ( op_str , " lsl " ) = = 0 ) { op_code = ASM_THUMB_FORMAT_4_LSL ; goto op_format_4 ;
} else if ( strcmp ( op_str , " lsr " ) = = 0 ) { op_code = ASM_THUMB_FORMAT_4_LSR ; goto op_format_4 ;
} else if ( strcmp ( op_str , " asr " ) = = 0 ) { op_code = ASM_THUMB_FORMAT_4_ASR ; goto op_format_4 ;
} else if ( strcmp ( op_str , " adc " ) = = 0 ) { op_code = ASM_THUMB_FORMAT_4_ADC ; goto op_format_4 ;
} else if ( strcmp ( op_str , " sbc " ) = = 0 ) { op_code = ASM_THUMB_FORMAT_4_SBC ; goto op_format_4 ;
} else if ( strcmp ( op_str , " ror " ) = = 0 ) { op_code = ASM_THUMB_FORMAT_4_ROR ; goto op_format_4 ;
} else if ( strcmp ( op_str , " tst " ) = = 0 ) { op_code = ASM_THUMB_FORMAT_4_TST ; goto op_format_4 ;
} else if ( strcmp ( op_str , " neg " ) = = 0 ) { op_code = ASM_THUMB_FORMAT_4_NEG ; goto op_format_4 ;
} else if ( strcmp ( op_str , " cmp " ) = = 0 ) { op_code = ASM_THUMB_FORMAT_4_CMP ; goto op_format_4 ;
} else if ( strcmp ( op_str , " cmn " ) = = 0 ) { op_code = ASM_THUMB_FORMAT_4_CMN ; goto op_format_4 ;
} else if ( strcmp ( op_str , " orr " ) = = 0 ) { op_code = ASM_THUMB_FORMAT_4_ORR ; goto op_format_4 ;
} else if ( strcmp ( op_str , " mul " ) = = 0 ) { op_code = ASM_THUMB_FORMAT_4_MUL ; goto op_format_4 ;
} else if ( strcmp ( op_str , " bic " ) = = 0 ) { op_code = ASM_THUMB_FORMAT_4_BIC ; goto op_format_4 ;
} else if ( strcmp ( op_str , " mvn " ) = = 0 ) { op_code = ASM_THUMB_FORMAT_4_MVN ; goto op_format_4 ;
} else {
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 ;
} else if ( strcmp ( op_str , " movw " ) = = 0 ) {
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 ] , 0xffff ) ;
asm_thumb_movw_reg_i16 ( emit - > as , reg_dest , i_src ) ;
} else if ( strcmp ( op_str , " movt " ) = = 0 ) {
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 ] , 0xffff ) ;
asm_thumb_movt_reg_i16 ( emit - > as , reg_dest , i_src ) ;
} else if ( strcmp ( op_str , " movwt " ) = = 0 ) {
// 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 ) ;
asm_thumb_movw_reg_i16 ( emit - > as , reg_dest , i_src & 0xffff ) ;
asm_thumb_movt_reg_i16 ( emit - > as , reg_dest , ( i_src > > 16 ) & 0x7fff ) ;
} else if ( strcmp ( op_str , " ldr " ) = = 0 ) {
op_code = ASM_THUMB_FORMAT_9_LDR | ASM_THUMB_FORMAT_9_WORD_TRANSFER ;
2014-09-08 23:05:16 +01:00
mp_uint_t rlo_dest , rlo_base , i5 ;
2014-04-13 00:30:32 +01:00
mp_parse_node_t pn_base , pn_offset ;
op_format_9_10 :
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 ) ) {
rlo_base = get_arg_reg ( emit , op_str , pn_base , 7 ) ;
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 ) ;
}
} else if ( strcmp ( op_str , " ldrb " ) = = 0 ) {
op_code = ASM_THUMB_FORMAT_9_LDR | ASM_THUMB_FORMAT_9_BYTE_TRANSFER ;
goto op_format_9_10 ;
} else if ( strcmp ( op_str , " ldrh " ) = = 0 ) {
op_code = ASM_THUMB_FORMAT_10_LDRH ;
goto op_format_9_10 ;
} else if ( strcmp ( op_str , " str " ) = = 0 ) {
op_code = ASM_THUMB_FORMAT_9_STR | ASM_THUMB_FORMAT_9_WORD_TRANSFER ;
goto op_format_9_10 ;
} else if ( strcmp ( op_str , " strb " ) = = 0 ) {
op_code = ASM_THUMB_FORMAT_9_STR | ASM_THUMB_FORMAT_9_BYTE_TRANSFER ;
goto op_format_9_10 ;
} else if ( strcmp ( op_str , " strh " ) = = 0 ) {
op_code = ASM_THUMB_FORMAT_10_STRH ;
goto op_format_9_10 ;
2015-02-12 22:52:42 +00:00
} else if ( strcmp ( op_str , " ldrex " ) = = 0 ) {
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 {
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 ;
2014-04-08 18:28:33 +01:00
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 ) ;
} else if ( strcmp ( op_str , " sub " ) = = 0 ) {
op_code = ASM_THUMB_FORMAT_2_SUB ;
goto op_format_2 ;
2015-02-12 22:52:42 +00:00
} else if ( strcmp ( op_str , " strex " ) = = 0 ) {
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 ) ) ;
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