1
/*====================================================================
3
$Id: assemble.cpp,v 1.3 2008-11-11 01:04:26 wntrmute Exp $
5
project: GameCube DSP Tool (gcdsp)
8
Copyright (c) 2005 Duddie
10
This program is free software; you can redistribute it and/or
11
modify it under the terms of the GNU General Public License
12
as published by the Free Software Foundation; either version 2
13
of the License, or (at your option) any later version.
15
This program is distributed in the hope that it will be useful,
16
but WITHOUT ANY WARRANTY; without even the implied warranty of
17
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18
GNU General Public License for more details.
20
You should have received a copy of the GNU General Public License
21
along with this program; if not, write to the Free Software
22
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
24
Revision 1.4 2008/10/04 10:30:00 Hermes
25
added function to export the code to .h file
26
added support for '/ *' '* /' and '//' for comentaries
27
added some sintax detection when use registers
29
$Log: not supported by cvs2svn $
30
Revision 1.2 2005/09/14 02:19:29 wntrmute
32
use standard main function
34
Revision 1.1 2005/08/24 22:13:34 wntrmute
38
====================================================================*/
49
#include "DSPInterpreter.h"
50
#include "DSPTables.h"
51
#include "disassemble.h"
54
static const char *err_string[] =
59
"Not enough parameters",
60
"Too many parameters",
62
"Expected parameter of type 'string'",
63
"Expected parameter of type 'value'",
64
"Expected parameter of type 'register'",
65
"Expected parameter of type 'memory pointer'",
66
"Expected parameter of type 'immediate'",
67
"Incorrect binary value",
68
"Incorrect hexadecimal value",
69
"Incorrect decimal value",
70
"Label already exists",
72
"No matching brackets",
73
"This opcode cannot be extended",
74
"Given extending params for non extensible opcode",
75
"Wrong parameter: must be accumulator register",
76
"Wrong parameter: must be mid accumulator register",
81
DSPAssembler::DSPAssembler(const AssemblerSettings &settings) :
91
DSPAssembler::~DSPAssembler()
97
bool DSPAssembler::Assemble(const char *text, std::vector<u16> &code, std::vector<int> *line_numbers)
100
line_numbers->clear();
101
const char *fname = "tmp.asm";
102
if (!File::WriteStringToFile(true, text, fname))
105
if (!AssembleFile(fname, 1))
108
// We now have the size of the output buffer
111
gdg_buffer = (char *)malloc(m_totalSize * sizeof(u16) + 4);
115
memset(gdg_buffer, 0, m_totalSize * sizeof(u16));
120
if (!AssembleFile(fname, 2))
123
code.resize(m_totalSize);
124
for (int i = 0; i < m_totalSize; i++) {
125
code[i] = *(u16 *)(gdg_buffer + i * 2);
133
last_error_str = "(no errors)";
139
void DSPAssembler::ShowError(err_t err_code, const char *extra_info)
142
if (!settings_.force)
145
char error_buffer[1024];
146
char *buf_ptr = error_buffer;
147
buf_ptr += sprintf(buf_ptr, "%i : %s ", code_line, cur_line.c_str());
151
if (m_current_param == 0)
152
buf_ptr += sprintf(buf_ptr, "ERROR: %s Line: %d : %s\n", err_string[err_code], code_line, extra_info);
154
buf_ptr += sprintf(buf_ptr, "ERROR: %s Line: %d Param: %d : %s\n",
155
err_string[err_code], code_line, m_current_param, extra_info);
156
last_error_str = error_buffer;
157
last_error = err_code;
160
char *skip_spaces(char *ptr)
167
const char *skip_spaces(const char *ptr)
174
// Parse a standalone value - it can be a number in one of several formats or a label.
175
s32 DSPAssembler::ParseValue(const char *str)
177
bool negative = false;
179
const char *ptr = str;
184
negative = true; // Wow! Double # (needed one to get in here) negates???
193
if (ptr[1] >= '0' && ptr[1] <= '9')
195
for (int i = 0; ptr[i] != 0; i++)
198
if (ptr[i] >= '0' && ptr[i] <= '9')
201
ShowError(ERR_INCORRECT_DEC, str);
209
for (int i = 2 ; ptr[i] != 0 ; i++)
212
if (ptr[i] >= 'a' && ptr[i] <= 'f')
213
val += (ptr[i]-'a'+10);
214
else if (ptr[i] >= 'A' && ptr[i] <= 'F')
215
val += (ptr[i]-'A'+10);
216
else if (ptr[i] >= '0' && ptr[i] <= '9')
217
val += (ptr[i] - '0');
219
ShowError(ERR_INCORRECT_HEX, str);
223
for (int i = 2; ptr[i] != 0; i++)
226
if(ptr[i] >= '0' && ptr[i] <= '1')
229
ShowError(ERR_INCORRECT_BIN, str);
233
// value is 0 or error
241
// Symbol starts with a digit - it's a dec number.
242
if (ptr[0] >= '0' && ptr[0] <= '9')
244
for (int i = 0; ptr[i] != 0; i++)
247
if (ptr[i] >= '0' && ptr[i] <= '9')
250
ShowError(ERR_INCORRECT_DEC, str);
253
else // Everything else is a label.
257
if (labels.GetLabelValue(ptr, &value))
260
ShowError(ERR_UNKNOWN_LABEL, str);
268
// Modifies both src and dst!
269
// What does it do, really??
270
char *DSPAssembler::FindBrackets(char *src, char *dst)
272
s32 len = (s32) strlen(src);
277
for (i = 0 ; i < len ; i++)
293
else if (src[i] == ')')
312
ShowError(ERR_NO_MATCHING_BRACKETS);
316
// Bizarre in-place expression evaluator.
317
u32 DSPAssembler::ParseExpression(const char *ptr)
322
char *d_buffer = (char *)malloc(1024);
323
char *s_buffer = (char *)malloc(1024);
324
strcpy(s_buffer, ptr);
326
while ((pbuf = FindBrackets(s_buffer, d_buffer)) != NULL)
328
val = ParseExpression(d_buffer);
329
sprintf(d_buffer, "%s%d%s", s_buffer, val, pbuf);
330
strcpy(s_buffer, d_buffer);
334
for (int i = 0; i < ((s32)strlen(s_buffer) + 1) ; i++)
336
char c = s_buffer[i];
341
for (int i = 0; i < ((s32)strlen(d_buffer) + 1) ; i++)
343
char c = d_buffer[i];
350
switch (d_buffer[i - 1])
362
while ((pbuf = strstr(d_buffer, "+")) != NULL)
365
val = ParseExpression(d_buffer) + ParseExpression(pbuf+1);
366
sprintf(d_buffer, "%d", val);
369
while ((pbuf = strstr(d_buffer, "-")) != NULL)
372
val = ParseExpression(d_buffer) - ParseExpression(pbuf+1);
375
val = 0x10000 + (val & 0xffff); // ATTENTION: avoid a terrible bug!!! number cannot write with '-' in sprintf
376
fprintf(stderr, "WARNING: Number Underflow at Line: %d \n", code_line);
378
sprintf(d_buffer, "%d", val);
381
while ((pbuf = strstr(d_buffer, "*")) != NULL)
384
val = ParseExpression(d_buffer) * ParseExpression(pbuf+1);
385
sprintf(d_buffer, "%d", val);
388
while ((pbuf = strstr(d_buffer, "/")) != NULL)
391
val = ParseExpression(d_buffer) / ParseExpression(pbuf+1);
392
sprintf(d_buffer, "%d", val);
395
while ((pbuf = strstr(d_buffer, "|")) != NULL)
398
val = ParseExpression(d_buffer) | ParseExpression(pbuf+1);
399
sprintf(d_buffer, "%d", val);
402
while ((pbuf = strstr(d_buffer, "&")) != NULL)
405
val = ParseExpression(d_buffer) & ParseExpression(pbuf+1);
406
sprintf(d_buffer, "%d", val);
409
val = ParseValue(d_buffer);
416
u32 DSPAssembler::GetParams(char *parstr, param_t *par)
419
char *tmpstr = skip_spaces(parstr);
420
tmpstr = strtok(tmpstr, ",\x00");
421
for (int i = 0; i < 10; i++)
425
tmpstr = skip_spaces(tmpstr);
426
if (strlen(tmpstr) == 0)
433
par[i].type = P_NONE;
437
par[i].str = strtok(tmpstr, "\"");
441
par[i].val = ParseExpression(tmpstr + 1);
445
if (tmpstr[1] == '$')
447
par[i].val = ParseExpression(tmpstr + 2);
452
par[i].val = ParseExpression(tmpstr + 1);
457
par[i].val = ParseExpression(tmpstr + 1);
462
par[i].val = ParseExpression(tmpstr);
466
tmpstr = strtok(NULL, ",\x00");
471
const opc_t *DSPAssembler::FindOpcode(const char *opcode, u32 par_count, const opc_t * const opcod, int opcod_size)
473
if (opcode[0] == 'C' && opcode[1] == 'W')
476
AliasMap::const_iterator alias_iter = aliases.find(opcode);
477
if (alias_iter != aliases.end())
478
opcode = alias_iter->second.c_str();
479
for (int i = 0; i < opcod_size; i++)
481
const opc_t *opc = &opcod[i];
482
if (strcmp(opc->name, opcode) == 0)
484
if (par_count < opc->param_count)
486
ShowError(ERR_NOT_ENOUGH_PARAMETERS);
488
if (par_count > opc->param_count)
490
ShowError(ERR_TOO_MANY_PARAMETERS);
495
ShowError(ERR_UNKNOWN_OPCODE);
500
u16 get_mask_shifted_down(u16 mask)
507
bool DSPAssembler::VerifyParams(const opc_t *opc, param_t *par, int count, bool ext)
509
for (int i = 0; i < count; i++)
511
const int current_param = i + 1; // just for display.
512
if (opc->params[i].type != par[i].type || (par[i].type & P_REG))
514
if (par[i].type == P_VAL &&
515
(opc->params[i].type == P_ADDR_I || opc->params[i].type == P_ADDR_D))
517
// Data and instruction addresses are valid as VAL values.
521
if ((opc->params[i].type & P_REG) && (par[i].type & P_REG))
523
// Just a temp. Should be replaced with more purposeful vars.
526
// modified by Hermes: test the register range
527
switch ((unsigned)opc->params[i].type)
532
value = (opc->params[i].type >> 8) & 31;
533
if ((int)par[i].val < value ||
534
(int)par[i].val > value + get_mask_shifted_down(opc->params[i].mask))
536
if (ext) fprintf(stderr, "(ext) ");
537
fprintf(stderr, "%s (param %i)", cur_line.c_str(), current_param);
538
ShowError(ERR_INVALID_REGISTER);
542
if ((int)par[i].val < 0 || (int)par[i].val > 0x3)
544
if (ext) fprintf(stderr, "(ext) ");
545
fprintf(stderr, "%s (param %i)", cur_line.c_str(), current_param);
546
ShowError(ERR_INVALID_REGISTER);
550
if ((int)par[i].val < 0x20 || (int)par[i].val > 0x21)
552
if (ext) fprintf(stderr, "(ext) ");
553
if (par[i].val >= 0x1e && par[i].val <= 0x1f) {
554
fprintf(stderr, "%i : %s ", code_line, cur_line.c_str());
555
fprintf(stderr, "WARNING: $ACM%d register used instead of $ACC%d register Line: %d Param: %d Ext: %d\n",
556
(par[i].val & 1), (par[i].val & 1), code_line, current_param, ext);
558
else if (par[i].val >= 0x1c && par[i].val <= 0x1d) {
559
fprintf(stderr, "WARNING: $ACL%d register used instead of $ACC%d register Line: %d Param: %d\n",
560
(par[i].val & 1), (par[i].val & 1), code_line, current_param);
563
ShowError(ERR_WRONG_PARAMETER_ACC);
567
if ((int)par[i].val < 0x1e || (int)par[i].val > 0x1f)
569
if (ext) fprintf(stderr, "(ext) ");
570
if (par[i].val >= 0x1c && par[i].val <= 0x1d)
571
fprintf(stderr, "WARNING: $ACL%d register used instead of $ACM%d register Line: %d Param: %d\n",
572
(par[i].val & 1), (par[i].val & 1), code_line, current_param);
573
else if (par[i].val >= 0x20 && par[i].val <= 0x21)
574
fprintf(stderr, "WARNING: $ACC%d register used instead of $ACM%d register Line: %d Param: %d\n",
575
(par[i].val & 1), (par[i].val & 1), code_line, current_param);
577
ShowError(ERR_WRONG_PARAMETER_ACC);
582
if ((int)par[i].val < 0x1c || (int)par[i].val > 0x1d)
584
if (ext) fprintf(stderr, "(ext) ");
585
if (par[i].val >= 0x1e && par[i].val <= 0x1f)
587
fprintf(stderr, "%s ", cur_line.c_str());
588
fprintf(stderr, "WARNING: $ACM%d register used instead of $ACL%d register Line: %d Param: %d\n",
589
(par[i].val & 1), (par[i].val & 1), code_line, current_param);
591
else if (par[i].val >= 0x20 && par[i].val <= 0x21) {
592
fprintf(stderr, "%s ", cur_line.c_str());
593
fprintf(stderr, "WARNING: $ACC%d register used instead of $ACL%d register Line: %d Param: %d\n",
594
(par[i].val & 1), (par[i].val & 1), code_line, current_param);
597
ShowError(ERR_WRONG_PARAMETER_ACC);
600
/* case P_ACCM_D: //P_ACC_MID:
601
if ((int)par[i].val < 0x1e || (int)par[i].val > 0x1f)
603
ShowError(ERR_WRONG_PARAMETER_MID_ACC);
610
switch (par[i].type & (P_REG | 7))
613
if (ext) fprintf(stderr, "(ext) ");
614
ShowError(ERR_EXPECTED_PARAM_REG);
617
if (ext) fprintf(stderr, "(ext) ");
618
ShowError(ERR_EXPECTED_PARAM_MEM);
621
if (ext) fprintf(stderr, "(ext) ");
622
ShowError(ERR_EXPECTED_PARAM_VAL);
625
if (ext) fprintf(stderr, "(ext) ");
626
ShowError(ERR_EXPECTED_PARAM_IMM);
629
ShowError(ERR_WRONG_PARAMETER);
632
else if ((opc->params[i].type & 3) != 0 && (par[i].type & 3) != 0)
634
// modified by Hermes: test NUMBER range
635
int value = get_mask_shifted_down(opc->params[i].mask);
636
unsigned int valueu = 0xffff & ~(value >> 1);
637
if ((int)par[i].val < 0)
639
if (value == 7) // value 7 por sbclr/sbset
641
fprintf(stderr,"Value must be from 0x0 to 0x%x\n", value);
642
ShowError(ERR_OUT_RANGE_NUMBER);
644
else if (opc->params[i].type == P_MEM)
647
fprintf(stderr, "Address value must be from 0x%x to 0x%x\n",valueu, (value>>1));
649
fprintf(stderr, "Address value must be from 0x0 to 0x%x\n", value);
651
ShowError(ERR_OUT_RANGE_NUMBER);
653
else if ((int)par[i].val < -((value >> 1) + 1))
656
fprintf(stderr, "Value must be from -0x%x to 0x%x, is %i\n",
657
(value >> 1) + 1, value >> 1, par[i].val);
659
fprintf(stderr, "Value must be from -0x%x to 0x%x or 0x0 to 0x%x, is %i\n",
660
(value >> 1) + 1, value >> 1, value, par[i].val);
662
ShowError(ERR_OUT_RANGE_NUMBER);
667
if (value == 7) // value 7 por sbclr/sbset
669
if (par[i].val > (unsigned)value)
671
fprintf(stderr,"Value must be from 0x%x to 0x%x, is %i\n",valueu, value, par[i].val);
672
ShowError(ERR_OUT_RANGE_NUMBER);
675
else if (opc->params[i].type == P_MEM)
678
value >>= 1; // addressing 8 bit with sign
679
if (par[i].val > (unsigned)value &&
680
(par[i].val < valueu || par[i].val > (unsigned)0xffff))
683
fprintf(stderr,"Address value must be from 0x%x to 0x%x, is %04x\n", valueu, value, par[i].val);
685
fprintf(stderr,"Address value must be minor of 0x%x\n", value+1);
686
ShowError(ERR_OUT_RANGE_NUMBER);
692
value >>= 1; // special case ASL/ASR/LSL/LSR
693
if (par[i].val > (unsigned)value)
696
fprintf(stderr,"Value must be from -0x%x to 0x%x, is %i\n", (value + 1), value, par[i].val);
698
fprintf(stderr,"Value must be minor of 0x%x, is %i\n", value + 1, par[i].val);
699
ShowError(ERR_OUT_RANGE_NUMBER);
711
// Merge opcode with params.
712
void DSPAssembler::BuildCode(const opc_t *opc, param_t *par, u32 par_count, u16 *outbuf)
714
outbuf[m_cur_addr] |= opc->opcode;
715
for (u32 i = 0; i < par_count; i++)
717
// Ignore the "reverse" parameters since they are implicit.
718
if (opc->params[i].type != P_ACC_D && opc->params[i].type != P_ACCM_D)
720
u16 t16 = outbuf[m_cur_addr + opc->params[i].loc];
721
u16 v16 = par[i].val;
722
if (opc->params[i].lshift > 0)
723
v16 <<= opc->params[i].lshift;
725
v16 >>= -opc->params[i].lshift;
726
v16 &= opc->params[i].mask;
727
outbuf[m_cur_addr + opc->params[i].loc] = t16 | v16;
732
void DSPAssembler::InitPass(int pass)
737
// Reset label table. Pre-populate with hw addresses and registers.
739
labels.RegisterDefaults();
741
aliases["S15"] = "SET15";
742
aliases["S16"] = "SET16";
743
aliases["S40"] = "SET40";
747
cur_segment = SEGMENT_CODE;
748
segment_addr[SEGMENT_CODE] = 0;
749
segment_addr[SEGMENT_DATA] = 0;
750
segment_addr[SEGMENT_OVERLAY] = 0;
753
bool DSPAssembler::AssembleFile(const char *fname, int pass)
755
int disable_text = 0; // modified by Hermes
758
OpenFStream(fsrc, fname, std::ios_base::in);
762
std::cerr << "Cannot open file " << fname << std::endl;
766
//printf("%s: Pass %d\n", fname, pass);
770
#define LINEBUF_SIZE 1024
771
char line[LINEBUF_SIZE] = {0};
772
while (!failed && !fsrc.fail() && !fsrc.eof())
775
fsrc.getline(line, LINEBUF_SIZE);
780
//printf("A: %s\n", line);
783
param_t params[10] = {{0, P_NONE, NULL}};
784
param_t params_ext[10] = {{0, P_NONE, NULL}};
787
for (int i = 0; i < LINEBUF_SIZE; i++)
790
// This stuff handles /**/ and // comments.
791
// modified by Hermes : added // and /* */ for long commentaries
796
if (line[i+1] == '/')
798
else if (line[i+1] == '*')
800
// toggle comment mode.
801
disable_text = !disable_text;
807
if (i < 1023 && line[i+1] == '/' && disable_text)
815
// turn text into spaces if disable_text is on (in a comment).
816
if (disable_text && ((unsigned char)c) > 32) c = 32;
818
if (c == 0x0a || c == 0x0d || c == ';')
820
if (c == 0x09) // tabs to spaces
824
if (upper && c >= 'a' && c <= 'z') // convert to uppercase
828
break; // modified by Hermes
834
size_t col_pos = std::string(line).find(":");
835
if (col_pos != std::string::npos)
839
for(int j = 0; j < (int)col_pos; j++)
842
if (!((ptr[j] >= 'A' && ptr[j] <= 'Z') || (ptr[j] == '_')))
844
if (!((ptr[j] >= '0' && ptr[j] <= '9') || (ptr[j] >= 'A' && ptr[j] <= 'Z') || (ptr[j] == '_')))
849
label = std::string(line).substr(0, col_pos);
855
opcode = strtok(ptr, " ");
856
char *opcode_ext = NULL;
858
u32 params_count = 0;
859
u32 params_count_ext = 0;
862
if ((opcode_ext = strstr(opcode, "'")) != NULL)
864
opcode_ext[0] = '\0';
866
if (strlen(opcode_ext) == 0)
869
// now we have opcode and label
872
params_count_ext = 0;
874
char *paramstr = strtok(NULL, "\0");
875
char *paramstr_ext = 0;
876
// there is valid opcode so probably we have parameters
880
if ((paramstr_ext = strstr(paramstr, ":")) != NULL)
882
paramstr_ext[0] = '\0';
888
params_count = GetParams(paramstr, params);
890
params_count_ext = GetParams(paramstr_ext, params_ext);
895
// there is a valid label so lets store it in labels table
896
u32 lval = m_cur_addr;
899
if (strcmp(opcode, "EQU") == 0)
901
lval = params[0].val;
906
labels.RegisterLabel(label, lval);
912
// check if opcode is reserved compiler word
913
if (strcmp("INCLUDE", opcode) == 0)
915
if (params[0].type == P_STR)
918
u32 thisCodeline = code_line;
920
if (include_dir.size())
922
tmpstr = (char *)malloc(include_dir.size() + strlen(params[0].str) + 2);
923
sprintf(tmpstr, "%s/%s", include_dir.c_str(), params[0].str);
927
tmpstr = (char *)malloc(strlen(params[0].str) + 1);
928
strcpy(tmpstr, params[0].str);
931
AssembleFile(tmpstr, pass);
933
code_line = thisCodeline;
938
ShowError(ERR_EXPECTED_PARAM_STR);
942
if (strcmp("INCDIR", opcode) == 0)
944
if (params[0].type == P_STR)
945
include_dir = params[0].str;
947
ShowError(ERR_EXPECTED_PARAM_STR);
951
if (strcmp("ORG", opcode) == 0)
953
if (params[0].type == P_VAL)
954
m_cur_addr = params[0].val;
956
ShowError(ERR_EXPECTED_PARAM_VAL);
960
if (strcmp("SEGMENT", opcode) == 0)
962
if (params[0].type == P_STR)
964
segment_addr[cur_segment] = m_cur_addr;
965
if (strcmp("DATA", params[0].str) == 0)
966
cur_segment = SEGMENT_DATA;
967
if (strcmp("CODE", params[0].str) == 0)
968
cur_segment = SEGMENT_CODE;
969
m_cur_addr = segment_addr[cur_segment];
972
ShowError(ERR_EXPECTED_PARAM_STR);
976
const opc_t *opc = FindOpcode(opcode, params_count, opcodes, opcodes_size);
980
opcode_size = opc->size;
982
VerifyParams(opc, params, params_count);
984
const opc_t *opc_ext = NULL;
985
// Check for opcode extensions.
990
opc_ext = FindOpcode(opcode_ext, params_count_ext, opcodes_ext, opcodes_ext_size);
991
VerifyParams(opc_ext, params_ext, params_count_ext, true);
993
else if (params_count_ext)
994
ShowError(ERR_EXT_PAR_NOT_EXT);
999
ShowError(ERR_EXT_CANT_EXTEND_OPCODE);
1000
if (params_count_ext)
1001
ShowError(ERR_EXT_PAR_NOT_EXT);
1007
((u16 *)gdg_buffer)[m_cur_addr] = 0x0000;
1008
BuildCode(opc, params, params_count, (u16 *)gdg_buffer);
1010
BuildCode(opc_ext, params_ext, params_count_ext, (u16 *)gdg_buffer);
1013
m_cur_addr += opcode_size;
1014
m_totalSize += opcode_size;