From 19c03d80d7d73eb5fbd86cbe7d4e85faca2c32a7 Mon Sep 17 00:00:00 2001 From: notaz Date: Wed, 22 Jun 2011 00:17:19 +0300 Subject: [PATCH 01/16] cdriso: handle TOC and track timing better --- libpcsxcore/cdriso.c | 182 +++++++++++++++++++++++++------------------ 1 file changed, 105 insertions(+), 77 deletions(-) diff --git a/libpcsxcore/cdriso.c b/libpcsxcore/cdriso.c index eb922812..cf1b3639 100644 --- a/libpcsxcore/cdriso.c +++ b/libpcsxcore/cdriso.c @@ -64,6 +64,8 @@ void CALLBACK CDR__about(void); long CALLBACK CDR__setfilename(char *filename); long CALLBACK CDR__getStatus(struct CdrStat *stat); +static void DecodeRawSubData(void); + extern void *hCDRDriver; struct trackinfo { @@ -71,6 +73,7 @@ struct trackinfo { char start[3]; // MSF-format char length[3]; // MSF-format FILE *handle; // for multi-track images CDDA + int start_offset; // sector offset from start of above file }; #define MAXTRACKS 100 /* How many tracks can a CD hold? */ @@ -181,12 +184,20 @@ static void *playthread(void *param) s += d; - // skip the subchannel data - fseek(cddaHandle, SUB_FRAMESIZE, SEEK_CUR); + fread(subbuffer, 1, SUB_FRAMESIZE, cddaHandle); + if (subChanRaw) DecodeRawSubData(); } } else { s = fread(sndbuffer, 1, sizeof(sndbuffer), cddaHandle); + + if (subHandle != NULL) { + fread(subbuffer, 1, SUB_FRAMESIZE, subHandle); + if (subChanRaw) DecodeRawSubData(); + + // a bit crude but ohwell + fseek(subHandle, (sizeof(sndbuffer) / CD_FRAMESIZE_RAW - 1) * SUB_FRAMESIZE, SEEK_CUR); + } } if (s == 0) { @@ -264,7 +275,7 @@ static int parsetoc(const char *isofile) { char linebuf[256], dummy[256], name[256]; char *token; char time[20], time2[20]; - unsigned int t; + unsigned int t, sector_offs; numtracks = 0; @@ -300,6 +311,8 @@ static int parsetoc(const char *isofile) { memset(&ti, 0, sizeof(ti)); cddaBigEndian = TRUE; // cdrdao uses big-endian for CD Audio + sector_offs = 2 * 75; + // parse the .toc file while (fgets(linebuf, sizeof(linebuf), fi) != NULL) { // search for tracks @@ -332,7 +345,8 @@ static int parsetoc(const char *isofile) { if (ti[numtracks].type == CDDA) { sscanf(linebuf, "DATAFILE \"%[^\"]\" #%d %8s", name, &t, time2); t /= CD_FRAMESIZE_RAW + (subChanMixed ? SUB_FRAMESIZE : 0); - t += 2 * 75; + ti[numtracks].start_offset = t; + t += sector_offs; sec2msf(t, (char *)&ti[numtracks].start); tok2msf((char *)&time2, (char *)&ti[numtracks].length); } @@ -345,10 +359,16 @@ static int parsetoc(const char *isofile) { sscanf(linebuf, "FILE \"%[^\"]\" #%d %8s %8s", name, &t, time, time2); tok2msf((char *)&time, (char *)&ti[numtracks].start); t /= CD_FRAMESIZE_RAW + (subChanMixed ? SUB_FRAMESIZE : 0); - t += msf2sec(ti[numtracks].start) + 2 * 75; + ti[numtracks].start_offset = t; + t += msf2sec(ti[numtracks].start) + sector_offs; sec2msf(t, (char *)&ti[numtracks].start); tok2msf((char *)&time2, (char *)&ti[numtracks].length); } + else if (!strcmp(token, "ZERO")) { + sscanf(linebuf, "ZERO AUDIO RW_RAW %8s", time); + tok2msf((char *)&time, dummy); + sector_offs += msf2sec(dummy); + } } fclose(fi); @@ -366,9 +386,9 @@ static int parsecue(const char *isofile) { char *token; char time[20]; char *tmp; - char linebuf[256], dummy[256]; + char linebuf[256], tmpb[256], dummy[256]; unsigned int incue_max_len; - unsigned int t, i, total, file_len; + unsigned int t, file_len, sector_offs; numtracks = 0; @@ -413,6 +433,9 @@ static int parsecue(const char *isofile) { memset(&ti, 0, sizeof(ti)); + file_len = 0; + sector_offs = 2 * 75; + while (fgets(linebuf, sizeof(linebuf), fi) != NULL) { strncpy(dummy, linebuf, sizeof(linebuf)); token = strtok(dummy, " "); @@ -421,7 +444,7 @@ static int parsecue(const char *isofile) { continue; } - if (!strcmp(token, "TRACK")){ + if (!strcmp(token, "TRACK")) { numtracks++; if (strstr(linebuf, "AUDIO") != NULL) { @@ -432,46 +455,64 @@ static int parsecue(const char *isofile) { } } else if (!strcmp(token, "INDEX")) { - tmp = strstr(linebuf, "INDEX"); - if (tmp != NULL) { - tmp += strlen("INDEX") + 3; // 3 - space + numeric index - while (*tmp == ' ') tmp++; - if (*tmp != '\n') sscanf(tmp, "%8s", time); - } + sscanf(linebuf, " INDEX %02d %8s", &t, time); + tok2msf(time, (char *)&ti[numtracks].start); - tok2msf((char *)&time, (char *)&ti[numtracks].start); - - t = msf2sec(ti[numtracks].start) + 2 * 75; + t = msf2sec(ti[numtracks].start); + ti[numtracks].start_offset = t; + t += sector_offs; sec2msf(t, ti[numtracks].start); - } - else if (!strcmp(token, "FILE")) { - tmp = strstr(linebuf, "FILE"); - if (tmp == NULL) { - continue; + + // default track length to file length + t = file_len - ti[numtracks].start_offset; + sec2msf(t, ti[numtracks].length); + + if (numtracks > 1 && ti[numtracks].handle == NULL) { + // this track uses the same file as the last, + // start of this track is last track's end + t = msf2sec(ti[numtracks].start) - msf2sec(ti[numtracks - 1].start); + sec2msf(t, ti[numtracks - 1].length); } - tmp += 4; - while (*tmp == ' ') - tmp++; - if (*tmp == '"') { - token = tmp + 1; - tmp = strchr(token, '"'); - if (tmp != NULL) - *tmp = 0; + } + else if (!strcmp(token, "PREGAP")) { + if (sscanf(linebuf, " PREGAP %8s", time) == 1) { + tok2msf(time, dummy); + sector_offs += msf2sec(dummy); } - else { - token = tmp; - tmp = strchr(token, ' '); + } + else if (!strcmp(token, "FILE")) { + sscanf(linebuf, " FILE \"%[^\"]\"", tmpb); + + // absolute path? + ti[numtracks + 1].handle = fopen(tmpb, "rb"); + if (ti[numtracks + 1].handle == NULL) { + // relative to .cue? + tmp = strrchr(tmpb, '\\'); + if (tmp == NULL) + tmp = strrchr(tmpb, '/'); if (tmp != NULL) - *tmp = 0; + tmp++; + else + tmp = tmpb; + strncpy(incue_fname, tmp, incue_max_len); + ti[numtracks + 1].handle = fopen(filepath, "rb"); } - strncpy(incue_fname, token, incue_max_len); - ti[numtracks + 1].handle = fopen(filepath, "rb"); + // update global offset if this is not first file in this .cue + if (numtracks + 1 > 1) + sector_offs += file_len; + + file_len = 0; if (ti[numtracks + 1].handle == NULL) { - SysPrintf(_("could not open: %s\n"), filepath); + SysPrintf(_("\ncould not open: %s\n"), filepath); + continue; } - else if (numtracks == 0 && strlen(isofile) >= 4 && - strcmp(isofile + strlen(isofile) - 4, ".cue") == 0) { + fseek(ti[numtracks + 1].handle, 0, SEEK_END); + file_len = ftell(ti[numtracks + 1].handle) / 2352; + + if (numtracks == 0 && strlen(isofile) >= 4 && + strcmp(isofile + strlen(isofile) - 4, ".cue") == 0) + { // user selected .cue as image file, use it's data track instead fclose(cdHandle); cdHandle = fopen(filepath, "rb"); @@ -481,29 +522,6 @@ static int parsecue(const char *isofile) { fclose(fi); - // make corrections for multi-track .cue, fill track lengths - total = 2 * 75; - file_len = 0; - for (i = 1; i <= numtracks; i++) { - if (ti[i].handle != NULL) { - sec2msf(total, ti[i].start); - fseek(ti[i].handle, 0, SEEK_END); - file_len = ftell(ti[i].handle) / 2352; - sec2msf(file_len, ti[i].length); - total += file_len; - } - else { - // this track uses the same file as the last, - // start of this track is last track's end - if (i > 1) { - t = msf2sec(ti[i].start) - msf2sec(ti[i - 1].start); - sec2msf(t, ti[i - 1].length); - } - t = file_len - msf2sec(ti[i].start) + 2 * 75; - sec2msf(t, ti[i].length); - } - } - return 0; } @@ -544,6 +562,7 @@ static int parseccd(const char *isofile) { else if (!strncmp(linebuf, "INDEX 1=", 8)) { sscanf(linebuf, "INDEX 1=%d", &t); sec2msf(t + 2 * 75, ti[numtracks].start); + ti[numtracks].start_offset = t; // If we've already seen another track, this is its end if (numtracks > 1) { @@ -643,11 +662,6 @@ static int parsemds(const char *isofile) { ti[i].start[1] = fgetc(fi); ti[i].start[2] = fgetc(fi); - if (i > 1) { - l = msf2sec(ti[i].start); - sec2msf(l - 2 * 75, ti[i].start); // ??? - } - // get the track length fread(&extra_offset, 1, sizeof(unsigned int), fi); extra_offset = SWAP32(extra_offset); @@ -657,6 +671,12 @@ static int parsemds(const char *isofile) { l = SWAP32(l); sec2msf(l, ti[i].length); + // get track start offset (in .mdf) + fseek(fi, offset + 0x28, SEEK_SET); + fread(&l, 1, sizeof(unsigned int), fi); + l = SWAP32(l); + ti[i].start_offset = l / CD_FRAMESIZE_RAW; + offset += 0x50; } @@ -872,23 +892,25 @@ static void DecodeRawSubData(void) { // time: byte 0 - minute; byte 1 - second; byte 2 - frame // uses bcd format static long CALLBACK ISOreadTrack(unsigned char *time) { + int sector = MSF2SECT(btoi(time[0]), btoi(time[1]), btoi(time[2])); + if (cdHandle == NULL) { return -1; } if (subChanMixed) { - fseek(cdHandle, MSF2SECT(btoi(time[0]), btoi(time[1]), btoi(time[2])) * (CD_FRAMESIZE_RAW + SUB_FRAMESIZE) + 12, SEEK_SET); + fseek(cdHandle, sector * (CD_FRAMESIZE_RAW + SUB_FRAMESIZE) + 12, SEEK_SET); fread(cdbuffer, 1, DATA_SIZE, cdHandle); fread(subbuffer, 1, SUB_FRAMESIZE, cdHandle); if (subChanRaw) DecodeRawSubData(); } else { - fseek(cdHandle, MSF2SECT(btoi(time[0]), btoi(time[1]), btoi(time[2])) * CD_FRAMESIZE_RAW + 12, SEEK_SET); + fseek(cdHandle, sector * CD_FRAMESIZE_RAW + 12, SEEK_SET); fread(cdbuffer, 1, DATA_SIZE, cdHandle); if (subHandle != NULL) { - fseek(subHandle, MSF2SECT(btoi(time[0]), btoi(time[1]), btoi(time[2])) * SUB_FRAMESIZE, SEEK_SET); + fseek(subHandle, sector * SUB_FRAMESIZE, SEEK_SET); fread(subbuffer, 1, SUB_FRAMESIZE, subHandle); if (subChanRaw) DecodeRawSubData(); @@ -907,32 +929,38 @@ static unsigned char * CALLBACK ISOgetBuffer(void) { // sector: byte 0 - minute; byte 1 - second; byte 2 - frame // does NOT uses bcd format static long CALLBACK ISOplay(unsigned char *time) { - unsigned int i, sect; + unsigned int i, abs_sect; + int file_sect; if (numtracks <= 1) return 0; // find the track - sect = msf2sec((char *)time); + abs_sect = msf2sec((char *)time); for (i = numtracks; i > 1; i--) - if (msf2sec(ti[i].start) <= sect + 2 * 75) + if (msf2sec(ti[i].start) <= abs_sect + 2 * 75) break; + file_sect = ti[i].start_offset + (abs_sect - msf2sec(ti[i].start)); + if (file_sect < 0) + file_sect = 0; + // find the file that contains this track for (; i > 1; i--) if (ti[i].handle != NULL) break; - cddaStartOffset = msf2sec(ti[i].start); - sect -= cddaStartOffset - 2 * 75; + cddaStartOffset = abs_sect - file_sect; cddaHandle = ti[i].handle; if (SPU_playCDDAchannel != NULL) { if (subChanMixed) { - startCDDA(sect * (CD_FRAMESIZE_RAW + SUB_FRAMESIZE)); + startCDDA(file_sect * (CD_FRAMESIZE_RAW + SUB_FRAMESIZE)); } else { - startCDDA(sect * CD_FRAMESIZE_RAW); + startCDDA(file_sect * CD_FRAMESIZE_RAW); + if (subHandle != NULL) + fseek(subHandle, file_sect * SUB_FRAMESIZE, SEEK_SET); } } return 0; -- 2.39.2 From 8aa9144305f5397475b17ed3cbe3452c29f8240f Mon Sep 17 00:00:00 2001 From: notaz Date: Wed, 22 Jun 2011 15:20:36 +0300 Subject: [PATCH 02/16] cdriso: handle pregap read/play --- libpcsxcore/cdriso.c | 49 +++++++++++++++++++++++++++++++++++++------- 1 file changed, 42 insertions(+), 7 deletions(-) diff --git a/libpcsxcore/cdriso.c b/libpcsxcore/cdriso.c index cf1b3639..6df8b676 100644 --- a/libpcsxcore/cdriso.c +++ b/libpcsxcore/cdriso.c @@ -38,6 +38,7 @@ static FILE *subHandle = NULL; static boolean subChanMixed = FALSE; static boolean subChanRaw = FALSE; +static boolean subChanMissing = FALSE; static unsigned char cdbuffer[DATA_SIZE]; static unsigned char subbuffer[SUB_FRAMESIZE]; @@ -56,6 +57,11 @@ static boolean playing = FALSE; static boolean cddaBigEndian = FALSE; static unsigned int cddaCurOffset = 0; static unsigned int cddaStartOffset; +/* Frame offset into CD image where pregap data would be found if it was there. + * If a game seeks there we must *not* return subchannel data since it's + * not in the CD image, so that cdrom code can fake subchannel data instead. + * XXX: there could be multiple pregaps but PSX dumps only have one? */ +static unsigned int pregapOffset; char* CALLBACK CDR__getDriveLetter(void); long CALLBACK CDR__configure(void); @@ -368,6 +374,10 @@ static int parsetoc(const char *isofile) { sscanf(linebuf, "ZERO AUDIO RW_RAW %8s", time); tok2msf((char *)&time, dummy); sector_offs += msf2sec(dummy); + if (numtracks > 1) { + t = ti[numtracks - 1].start_offset; + pregapOffset = t + msf2sec(ti[numtracks - 1].length); + } } } @@ -473,12 +483,15 @@ static int parsecue(const char *isofile) { t = msf2sec(ti[numtracks].start) - msf2sec(ti[numtracks - 1].start); sec2msf(t, ti[numtracks - 1].length); } + if (numtracks > 1 && pregapOffset == -1) + pregapOffset = ti[numtracks].start_offset; } else if (!strcmp(token, "PREGAP")) { if (sscanf(linebuf, " PREGAP %8s", time) == 1) { tok2msf(time, dummy); sector_offs += msf2sec(dummy); } + pregapOffset = -1; // mark to fill track start_offset } else if (!strcmp(token, "FILE")) { sscanf(linebuf, " FILE \"%[^\"]\"", tmpb); @@ -662,21 +675,27 @@ static int parsemds(const char *isofile) { ti[i].start[1] = fgetc(fi); ti[i].start[2] = fgetc(fi); - // get the track length fread(&extra_offset, 1, sizeof(unsigned int), fi); extra_offset = SWAP32(extra_offset); - fseek(fi, extra_offset + 4, SEEK_SET); - fread(&l, 1, sizeof(unsigned int), fi); - l = SWAP32(l); - sec2msf(l, ti[i].length); - // get track start offset (in .mdf) fseek(fi, offset + 0x28, SEEK_SET); fread(&l, 1, sizeof(unsigned int), fi); l = SWAP32(l); ti[i].start_offset = l / CD_FRAMESIZE_RAW; + // get pregap + fseek(fi, extra_offset, SEEK_SET); + fread(&l, 1, sizeof(unsigned int), fi); + l = SWAP32(l); + if (l != 0 && i > 1) + pregapOffset = ti[i].start_offset; + + // get the track length + fread(&l, 1, sizeof(unsigned int), fi); + l = SWAP32(l); + sec2msf(l, ti[i].length); + offset += 0x50; } @@ -753,6 +772,8 @@ static long CALLBACK ISOopen(void) { cddaBigEndian = FALSE; subChanMixed = FALSE; subChanRaw = FALSE; + subChanMixed = FALSE; + pregapOffset = 0; if (parsecue(GetIsoFile()) == 0) { SysPrintf("[+cue]"); @@ -898,6 +919,15 @@ static long CALLBACK ISOreadTrack(unsigned char *time) { return -1; } + if (pregapOffset) { + subChanMissing = FALSE; + if (sector >= pregapOffset) { + sector -= 2 * 75; + if (sector < pregapOffset) + subChanMissing = TRUE; + } + } + if (subChanMixed) { fseek(cdHandle, sector * (CD_FRAMESIZE_RAW + SUB_FRAMESIZE) + 12, SEEK_SET); fread(cdbuffer, 1, DATA_SIZE, cdHandle); @@ -953,6 +983,11 @@ static long CALLBACK ISOplay(unsigned char *time) { cddaStartOffset = abs_sect - file_sect; cddaHandle = ti[i].handle; + if (pregapOffset && (unsigned int)(pregapOffset - file_sect) < 2 * 75) { + // get out of the missing pregap to avoid noise + file_sect = pregapOffset; + } + if (SPU_playCDDAchannel != NULL) { if (subChanMixed) { startCDDA(file_sect * (CD_FRAMESIZE_RAW + SUB_FRAMESIZE)); @@ -974,7 +1009,7 @@ static long CALLBACK ISOstop(void) { // gets subchannel data static unsigned char* CALLBACK ISOgetBufferSub(void) { - if (subHandle != NULL || subChanMixed) { + if ((subHandle != NULL || subChanMixed) && !subChanMissing) { return subbuffer; } -- 2.39.2 From 696c9530369f5f5b75a15f0b180d6bf8d855afa3 Mon Sep 17 00:00:00 2001 From: notaz Date: Wed, 22 Jun 2011 15:42:20 +0300 Subject: [PATCH 03/16] cdrom: remove play pregap hack cdriso code handles this now --- libpcsxcore/cdrom.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libpcsxcore/cdrom.c b/libpcsxcore/cdrom.c index c82b6daf..c667ac48 100644 --- a/libpcsxcore/cdrom.c +++ b/libpcsxcore/cdrom.c @@ -709,11 +709,12 @@ void cdrInterrupt() { Wild 9: skip PREGAP + starting accurate SubQ - plays tracks without retry play */ + /* unneeded with correct cdriso? Set_Track(); + */ Find_CurTrack(); ReadTrack( cdr.SetSectorPlay ); - // GameShark CD Player: Calls 2x + Play 2x if( cdr.FastBackward || cdr.FastForward ) { if( cdr.FastForward ) cdr.FastForward--; -- 2.39.2 From 20d507ba0e7695190b32b3a06d8024e3ab81d158 Mon Sep 17 00:00:00 2001 From: notaz Date: Wed, 22 Jun 2011 15:49:37 +0300 Subject: [PATCH 04/16] drc: merge Ari64's patch: 01_year_2011 --- libpcsxcore/new_dynarec/assem_arm.c | 2 +- libpcsxcore/new_dynarec/new_dynarec.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/libpcsxcore/new_dynarec/assem_arm.c b/libpcsxcore/new_dynarec/assem_arm.c index 539ee4ed..55eef842 100644 --- a/libpcsxcore/new_dynarec/assem_arm.c +++ b/libpcsxcore/new_dynarec/assem_arm.c @@ -1,6 +1,6 @@ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Mupen64plus - assem_arm.c * - * Copyright (C) 2009-2010 Ari64 * + * Copyright (C) 2009-2011 Ari64 * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * diff --git a/libpcsxcore/new_dynarec/new_dynarec.c b/libpcsxcore/new_dynarec/new_dynarec.c index 712a0340..2368a68a 100644 --- a/libpcsxcore/new_dynarec/new_dynarec.c +++ b/libpcsxcore/new_dynarec/new_dynarec.c @@ -1,6 +1,6 @@ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Mupen64plus - new_dynarec.c * - * Copyright (C) 2009-2010 Ari64 * + * Copyright (C) 2009-2011 Ari64 * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * -- 2.39.2 From b7217e13a765d299feaac0f7646880e53039d9e2 Mon Sep 17 00:00:00 2001 From: notaz Date: Wed, 22 Jun 2011 15:55:08 +0300 Subject: [PATCH 05/16] drc: merge Ari64's patch: 03_needed_again --- libpcsxcore/new_dynarec/new_dynarec.c | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/libpcsxcore/new_dynarec/new_dynarec.c b/libpcsxcore/new_dynarec/new_dynarec.c index 2368a68a..95e51042 100644 --- a/libpcsxcore/new_dynarec/new_dynarec.c +++ b/libpcsxcore/new_dynarec/new_dynarec.c @@ -719,12 +719,6 @@ int needed_again(int r, int i) int j; int b=-1; int rn=10; - int hr; - u_char hsn[MAXREG+1]; - int preferred_reg; - - memset(hsn,10,sizeof(hsn)); - lsn(hsn,i,&preferred_reg); if(i>0&&(itype[i-1]==UJUMP||itype[i-1]==RJUMP||(source[i-1]>>16)==0x1000)) { @@ -777,11 +771,7 @@ int needed_again(int r, int i) } } }*/ - for(hr=0;hr Date: Wed, 22 Jun 2011 16:02:04 +0300 Subject: [PATCH 06/16] drc: merge Ari64's patch: 04_constant_address_pagefaults causes problems in PCSX, so ifdefed out but left there to reduce drift from Ari64's version. --- libpcsxcore/new_dynarec/assem_arm.c | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/libpcsxcore/new_dynarec/assem_arm.c b/libpcsxcore/new_dynarec/assem_arm.c index 55eef842..dd4b070f 100644 --- a/libpcsxcore/new_dynarec/assem_arm.c +++ b/libpcsxcore/new_dynarec/assem_arm.c @@ -2739,6 +2739,18 @@ inline_readstub(int type, int i, u_int addr, signed char regmap[], int target, i emit_writeword(rs,(int)&address); //emit_pusha(); save_regs(reglist); +#ifndef PCSX + if((signed int)addr>=(signed int)0xC0000000) { + // Theoretically we can have a pagefault here, if the TLB has never + // been enabled and the address is outside the range 80000000..BFFFFFFF + // Write out the registers so the pagefault can be handled. This is + // a very rare case and likely represents a bug. + int ds=regmap!=regs[i].regmap; + if(!ds) load_all_consts(regs[i].regmap_entry,regs[i].was32,regs[i].wasdirty,i); + if(!ds) wb_dirtys(regs[i].regmap_entry,regs[i].was32,regs[i].wasdirty); + else wb_dirtys(branch_regs[i-1].regmap_entry,branch_regs[i-1].was32,branch_regs[i-1].wasdirty); + } +#endif //emit_shrimm(rs,16,1); int cc=get_reg(regmap,CCREG); if(cc<0) { @@ -2929,6 +2941,19 @@ inline_writestub(int type, int i, u_int addr, signed char regmap[], int target, } //emit_pusha(); save_regs(reglist); +#ifndef PCSX + // rearmed note: load_all_consts prevents BIOS boot, some bug? + if((signed int)addr>=(signed int)0xC0000000) { + // Theoretically we can have a pagefault here, if the TLB has never + // been enabled and the address is outside the range 80000000..BFFFFFFF + // Write out the registers so the pagefault can be handled. This is + // a very rare case and likely represents a bug. + int ds=regmap!=regs[i].regmap; + if(!ds) load_all_consts(regs[i].regmap_entry,regs[i].was32,regs[i].wasdirty,i); + if(!ds) wb_dirtys(regs[i].regmap_entry,regs[i].was32,regs[i].wasdirty); + else wb_dirtys(branch_regs[i-1].regmap_entry,branch_regs[i-1].was32,branch_regs[i-1].wasdirty); + } +#endif //emit_shrimm(rs,16,1); int cc=get_reg(regmap,CCREG); if(cc<0) { -- 2.39.2 From 79c75f1b7efbec9dc8bbd6e3546ec1c07040b702 Mon Sep 17 00:00:00 2001 From: notaz Date: Wed, 22 Jun 2011 16:07:01 +0300 Subject: [PATCH 07/16] drc: merge Ari64's patch: 11_dealloc_reg_clear_const_flag --- libpcsxcore/new_dynarec/new_dynarec.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/libpcsxcore/new_dynarec/new_dynarec.c b/libpcsxcore/new_dynarec/new_dynarec.c index 95e51042..66603ea8 100644 --- a/libpcsxcore/new_dynarec/new_dynarec.c +++ b/libpcsxcore/new_dynarec/new_dynarec.c @@ -599,6 +599,7 @@ void clear_const(struct regstat *cur,signed char reg) int is_const(struct regstat *cur,signed char reg) { int hr; + if(reg<0) return 0; if(!reg) return 1; for (hr=0;hrregmap[hr]&63)==reg) { @@ -9751,7 +9752,10 @@ int new_recompile_block(int addr) if(likely[i]) { regs[i].regmap[hr]=-1; regs[i].isconst&=~(1< Date: Wed, 22 Jun 2011 16:08:46 +0300 Subject: [PATCH 08/16] drc: merge Ari64's patch: 12_cycle_count --- libpcsxcore/new_dynarec/new_dynarec.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libpcsxcore/new_dynarec/new_dynarec.c b/libpcsxcore/new_dynarec/new_dynarec.c index 66603ea8..77273844 100644 --- a/libpcsxcore/new_dynarec/new_dynarec.c +++ b/libpcsxcore/new_dynarec/new_dynarec.c @@ -4857,7 +4857,7 @@ void do_cc(int i,signed char i_regmap[],int *adj,int addr,int taken,int invert) } else { - emit_cmpimm(HOST_CCREG,-2*(count+2)); + emit_cmpimm(HOST_CCREG,-CLOCK_DIVIDER*(count+2)); jaddr=(int)out; emit_jns(0); } -- 2.39.2 From 373d1d07514915c14224d6062e046c76743e4514 Mon Sep 17 00:00:00 2001 From: notaz Date: Wed, 22 Jun 2011 16:12:06 +0300 Subject: [PATCH 09/16] drc: merge Ari64's patch: 13_dummy_loads --- libpcsxcore/new_dynarec/new_dynarec.c | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/libpcsxcore/new_dynarec/new_dynarec.c b/libpcsxcore/new_dynarec/new_dynarec.c index 77273844..9592dd2b 100644 --- a/libpcsxcore/new_dynarec/new_dynarec.c +++ b/libpcsxcore/new_dynarec/new_dynarec.c @@ -1593,13 +1593,9 @@ void load_alloc(struct regstat *current,int i) //if(rs1[i]!=rt1[i]&&needed_again(rs1[i],i)) clear_const(current,rs1[i]); // Does this help or hurt? if(!rs1[i]) current->u&=~1LL; // Allow allocating r0 if it's the source register if(needed_again(rs1[i],i)) alloc_reg(current,i,rs1[i]); - if(rt1[i]) { + if(rt1[i]&&!((current->u>>rt1[i])&1)) { alloc_reg(current,i,rt1[i]); - if(get_reg(current->regmap,rt1[i])<0) { - // dummy load, but we still need a register to calculate the address - alloc_reg_temp(current,i,-1); - minimum_free_regs[i]=1; - } + assert(get_reg(current->regmap,rt1[i])>=0); if(opcode[i]==0x27||opcode[i]==0x37) // LWU/LD { current->is32&=~(1LL< Date: Wed, 22 Jun 2011 16:23:34 +0300 Subject: [PATCH 10/16] drc: merge Ari64's patch: 14_dont_save_or_restore_temporary --- libpcsxcore/new_dynarec/assem_arm.c | 1 + libpcsxcore/new_dynarec/new_dynarec.c | 22 +++++++++++++--------- 2 files changed, 14 insertions(+), 9 deletions(-) diff --git a/libpcsxcore/new_dynarec/assem_arm.c b/libpcsxcore/new_dynarec/assem_arm.c index dd4b070f..2b0ef9cc 100644 --- a/libpcsxcore/new_dynarec/assem_arm.c +++ b/libpcsxcore/new_dynarec/assem_arm.c @@ -3413,6 +3413,7 @@ void loadlr_assemble_arm(int i,struct regstat *i_regs) } map=get_reg(i_regs->regmap,TLREG); assert(map>=0); + reglist&=~(1<regmap,TLREG); assert(map>=0); + reglist&=~(1<regmap,TLREG); assert(map>=0); + reglist&=~(1<regmap,TLREG); assert(map>=0); + reglist&=~(1<=0) emit_mov(s,temp); do_tlb_w_branch(map,c,constmap[i][s]+offset,&jaddr); @@ -3602,6 +3605,7 @@ void c1ls_assemble(int i,struct regstat *i_regs) { map=get_reg(i_regs->regmap,TLREG); assert(map>=0); + reglist&=~(1<0 && i_regmap[hr]!=CCREG) + if(i_regmap[hr]>0 && (i_regmap[hr]&63)0 && i_regmap[hr]!=CCREG) + if(i_regmap[hr]>0 && (i_regmap[hr]&63)=0&®s[t].regmap_entry[hr]<64) { + if(regs[t].regmap_entry[hr]>=0&®s[t].regmap_entry[hr]=64) { + if(regs[t].regmap_entry[hr]>=64&®s[t].regmap_entry[hr]>(regs[t].regmap_entry[hr]&63))&1) { int lr=get_reg(regs[t].regmap_entry,regs[t].regmap_entry[hr]-64); @@ -4610,7 +4614,7 @@ void load_regs_bt(signed char i_regmap[],uint64_t i_is32,uint64_t i_dirty,int ad } // Load 32-bit regs for(hr=0;hr=0&®s[t].regmap_entry[hr]<64) { + if(hr!=EXCLUDE_REG&®s[t].regmap_entry[hr]>=0&®s[t].regmap_entry[hr]>hr)&1) && ((i_dirty>>hr)&1) && (((i_is32&~unneeded_reg_upper[t])>>i_regmap[hr])&1) ) || (((i_is32&~regs[t].was32&~unneeded_reg_upper[t])>>(i_regmap[hr]&63))&1)) { #else @@ -4628,7 +4632,7 @@ void load_regs_bt(signed char i_regmap[],uint64_t i_is32,uint64_t i_dirty,int ad } //Load 64-bit regs for(hr=0;hr=64) { + if(hr!=EXCLUDE_REG&®s[t].regmap_entry[hr]>=64&®s[t].regmap_entry[hr]>(regs[t].regmap_entry[hr]&63))&1) { @@ -4669,19 +4673,19 @@ int match_bt(signed char i_regmap[],uint64_t i_is32,uint64_t i_dirty,int addr) { if(i_regmap[hr]!=regs[t].regmap_entry[hr]) { - if(regs[t].regmap_entry[hr]!=-1) + if(regs[t].regmap_entry[hr]>=0&&(regs[t].regmap_entry[hr]|64)>hr)&1) { - if(i_regmap[hr]<64) + if(i_regmap[hr]>i_regmap[hr])&1)) return 0; } - else + else if(i_regmap[hr]>=64&&i_regmap[hr]>(i_regmap[hr]&63))&1)) return 0; -- 2.39.2 From f776eb14d490a2c4391666ce5beefe058e7e03d5 Mon Sep 17 00:00:00 2001 From: notaz Date: Wed, 22 Jun 2011 16:26:38 +0300 Subject: [PATCH 11/16] drc: merge Ari64's patch: 15_dirty_registers_fix --- libpcsxcore/new_dynarec/assem_arm.c | 24 +++++++----------------- libpcsxcore/new_dynarec/new_dynarec.c | 11 ++++++++--- 2 files changed, 15 insertions(+), 20 deletions(-) diff --git a/libpcsxcore/new_dynarec/assem_arm.c b/libpcsxcore/new_dynarec/assem_arm.c index 2b0ef9cc..af45ce37 100644 --- a/libpcsxcore/new_dynarec/assem_arm.c +++ b/libpcsxcore/new_dynarec/assem_arm.c @@ -762,15 +762,20 @@ void alloc_reg_temp(struct regstat *cur,int i,signed char reg) void alloc_arm_reg(struct regstat *cur,int i,signed char reg,char hr) { int n; + int dirty=0; // see if it's already allocated (and dealloc it) for(n=0;nregmap[n]==reg) {cur->regmap[n]=-1;} + if(n!=EXCLUDE_REG&&cur->regmap[n]==reg) { + dirty=(cur->dirty>>n)&1; + cur->regmap[n]=-1; + } } cur->regmap[hr]=reg; cur->dirty&=~(1<dirty|=dirty<isconst&=~(1<>(reg&63))&1) { - if(reg==entry[hr]||(reg>0&&entry[hr]<0)) { + if(reg>0) { if(((dirty_pre&~dirty)>>hr)&1) { if(reg>0&®<34) { emit_storereg(reg,hr); @@ -4812,21 +4817,6 @@ void wb_valid(signed char pre[],signed char entry[],u_int dirty_pre,u_int dirty, } } } - else // Check if register moved to a different register - if((new_hr=get_reg(entry,reg))>=0) { - if((dirty_pre>>hr)&(~dirty>>new_hr)&1) { - if(reg>0&®<34) { - emit_storereg(reg,hr); - if( ((is32_pre&~uu)>>reg)&1 ) { - emit_sarimm(hr,31,HOST_TEMPREG); - emit_storereg(reg|64,HOST_TEMPREG); - } - } - else if(reg>=64) { - emit_storereg(reg,hr); - } - } - } } } } diff --git a/libpcsxcore/new_dynarec/new_dynarec.c b/libpcsxcore/new_dynarec/new_dynarec.c index 7d4a3d92..f1bdbfc2 100644 --- a/libpcsxcore/new_dynarec/new_dynarec.c +++ b/libpcsxcore/new_dynarec/new_dynarec.c @@ -7642,7 +7642,7 @@ void clean_registers(int istart,int iend,int wr) regs[i].wasdirty|=will_dirty_i&(1<=0) { + else if(regmap_pre[i][r]>=0&&(nr=get_reg(regs[i].regmap,regmap_pre[i][r]))>=0) { // Register moved to a different register will_dirty_i&=~(1<>16)!=0x1000)) -- 2.39.2 From 2a70696411b92af26a72c2b28212e4fe73cdd2d8 Mon Sep 17 00:00:00 2001 From: notaz Date: Wed, 22 Jun 2011 17:17:42 +0300 Subject: [PATCH 12/16] drc: merge Ari64's patch: 16_continue_after_branch_into_delay_slot --- libpcsxcore/new_dynarec/new_dynarec.c | 1 + 1 file changed, 1 insertion(+) diff --git a/libpcsxcore/new_dynarec/new_dynarec.c b/libpcsxcore/new_dynarec/new_dynarec.c index f1bdbfc2..9fdaa72b 100644 --- a/libpcsxcore/new_dynarec/new_dynarec.c +++ b/libpcsxcore/new_dynarec/new_dynarec.c @@ -8577,6 +8577,7 @@ int new_recompile_block(int addr) // Does the block continue due to a branch? for(j=i-1;j>=0;j--) { + if(ba[j]==start+i*4) done=j=0; // Branch into delay slot if(ba[j]==start+i*4+4) done=j=0; if(ba[j]==start+i*4+8) done=j=0; } -- 2.39.2 From e3234ecf9665738e35a749fbb9d4120f25a0c7cf Mon Sep 17 00:00:00 2001 From: notaz Date: Wed, 22 Jun 2011 17:46:24 +0300 Subject: [PATCH 13/16] drc: merge Ari64's patch: 17_branch_target_liveness_analysis --- libpcsxcore/new_dynarec/new_dynarec.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/libpcsxcore/new_dynarec/new_dynarec.c b/libpcsxcore/new_dynarec/new_dynarec.c index 9fdaa72b..c6d83c2c 100644 --- a/libpcsxcore/new_dynarec/new_dynarec.c +++ b/libpcsxcore/new_dynarec/new_dynarec.c @@ -7453,6 +7453,10 @@ void clean_registers(int istart,int iend,int wr) will_dirty_i|=will_dirty[(ba[i]-start)>>2]&(1<>2]&(1<=0) { + will_dirty_i|=((unneeded_reg[(ba[i]-start)>>2]>>(branch_regs[i].regmap[r]&63))&1)<>2]>>(branch_regs[i].regmap[r]&63))&1)<start+i*4) { // Disable recursion (for debugging) for(r=0;r>2].regmap_entry[r]) { + signed char target_reg=branch_regs[i].regmap[r]; + if(target_reg==regs[(ba[i]-start)>>2].regmap_entry[r]) { will_dirty_i&=will_dirty[(ba[i]-start)>>2]&(1<>2]&(1<=0) { + will_dirty_i&=((unneeded_reg[(ba[i]-start)>>2]>>(target_reg&63))&1)<>2]>>(target_reg&63))&1)<>2].regmap_entry[r]) { @@ -7525,7 +7530,7 @@ void clean_registers(int istart,int iend,int wr) } } } - // Merge in delay slot + // Merge in delay slot (won't dirty) for(r=0;r Date: Wed, 22 Jun 2011 18:47:10 +0300 Subject: [PATCH 14/16] drc: merge part of old Ari64's patch: 09_tlb_offset This one is from previous batch, applying so that the next patch applies. --- libpcsxcore/new_dynarec/new_dynarec.c | 168 ++++++++++++++++++++++++++ 1 file changed, 168 insertions(+) diff --git a/libpcsxcore/new_dynarec/new_dynarec.c b/libpcsxcore/new_dynarec/new_dynarec.c index c6d83c2c..74b64b77 100644 --- a/libpcsxcore/new_dynarec/new_dynarec.c +++ b/libpcsxcore/new_dynarec/new_dynarec.c @@ -10228,6 +10228,174 @@ int new_recompile_block(int addr) } } + // Cache memory offset or tlb map pointer if a register is available + #ifndef HOST_IMM_ADDR32 + #ifndef RAM_OFFSET + if(using_tlb) + #endif + { + int earliest_available[HOST_REGS]; + int loop_start[HOST_REGS]; + int score[HOST_REGS]; + int end[HOST_REGS]; + int reg=using_tlb?MMREG:ROREG; + + // Init + for(hr=0;hr=0) { + score[hr]=0;earliest_available[hr]=i+1; + loop_start[hr]=MAXBLOCK; + } + if(itype[i]==UJUMP||itype[i]==RJUMP||itype[i]==CJUMP||itype[i]==SJUMP||itype[i]==FJUMP) { + if(branch_regs[i].regmap[hr]>=0) { + score[hr]=0;earliest_available[hr]=i+2; + loop_start[hr]=MAXBLOCK; + } + } + } + // No register allocations after unconditional jumps + if(itype[i]==UJUMP||itype[i]==RJUMP||(source[i]>>16)==0x1000) + { + for(hr=0;hr=0) break; + if(itype[j]==UJUMP||itype[j]==RJUMP||itype[j]==CJUMP||itype[j]==SJUMP||itype[j]==FJUMP) { + if(branch_regs[j].regmap[hr]>=0) break; + if(ooo[j]) { + if(count_free_regs(regs[j].regmap)<=minimum_free_regs[j+1]) break; + }else{ + if(count_free_regs(branch_regs[j].regmap)<=minimum_free_regs[j+1]) break; + } + } + else if(count_free_regs(regs[j].regmap)<=minimum_free_regs[j]) break; + if(itype[j]==UJUMP||itype[j]==RJUMP||itype[j]==CJUMP||itype[j]==SJUMP||itype[j]==FJUMP) { + int t=(ba[j]-start)>>2; + if(t=earliest_available[hr]) { + // Score a point for hoisting loop invariant + if(t>16)==0x1000) + { + // Stop on unconditional branch + break; + } + else + if(itype[j]==LOAD||itype[j]==LOADLR|| + itype[j]==STORE||itype[j]==STORELR||itype[j]==C1LS) { + score[hr]++; + end[hr]=j; + } + } + } + } + // Find highest score and allocate that register + int maxscore=0; + for(hr=0;hrscore[maxscore]) { + maxscore=hr; + //printf("highest score: %d %d (%x->%x)\n",score[hr],hr,start+i*4,start+end[hr]*4); + } + } + } + if(score[maxscore]>1) + { + if(i=0) {printf("oops: %x %x was %d=%d\n",loop_start[maxscore]*4+start,j*4+start,maxscore,regs[j].regmap[maxscore]);} + assert(regs[j].regmap[maxscore]<0); + if(j>loop_start[maxscore]) regs[j].regmap_entry[maxscore]=reg; + regs[j].regmap[maxscore]=reg; + regs[j].dirty&=~(1<>16)!=0x1000) { + regmap_pre[j+2][maxscore]=reg; + regs[j+2].wasdirty&=~(1<>2; + if(t==loop_start[maxscore]) regs[t].regmap_entry[maxscore]=reg; + } + else + { + if(j<1||(itype[j-1]!=RJUMP&&itype[j-1]!=UJUMP&&itype[j-1]!=CJUMP&&itype[j-1]!=SJUMP&&itype[j-1]!=FJUMP)) { + regmap_pre[j+1][maxscore]=reg; + regs[j+1].wasdirty&=~(1< Date: Wed, 22 Jun 2011 18:52:24 +0300 Subject: [PATCH 15/16] drc: merge Ari64's patch: 18_loop_preload_fix --- libpcsxcore/new_dynarec/new_dynarec.c | 39 ++++++++++++++++++--------- 1 file changed, 27 insertions(+), 12 deletions(-) diff --git a/libpcsxcore/new_dynarec/new_dynarec.c b/libpcsxcore/new_dynarec/new_dynarec.c index 74b64b77..b0c0b4c1 100644 --- a/libpcsxcore/new_dynarec/new_dynarec.c +++ b/libpcsxcore/new_dynarec/new_dynarec.c @@ -9880,7 +9880,7 @@ int new_recompile_block(int addr) // If a register is allocated during a loop, try to allocate it for the // entire loop, if possible. This avoids loading/storing registers // inside of the loop. - + signed char f_regmap[HOST_REGS]; clear_all_regs(f_regmap); for(i=0;i>2; if(t>0&&(itype[t-1]!=UJUMP&&itype[t-1]!=RJUMP&&itype[t-1]!=CJUMP&&itype[t-1]!=SJUMP&&itype[t-1]!=FJUMP)) // loop_preload can't handle jumps into delay slots - if(t<2||(itype[t-2]!=UJUMP)) // call/ret assumes no registers allocated + if(t<2||(itype[t-2]!=UJUMP&&itype[t-2]!=RJUMP)||rt1[t-2]!=31) // call/ret assumes no registers allocated for(hr=0;hr64) { @@ -9953,7 +9953,7 @@ int new_recompile_block(int addr) // a mov, which is of negligible benefit. So such cases are // skipped below. if(f_regmap[hr]>0) { - if(regs[t].regmap_entry[hr]<0&&get_reg(regmap_pre[t],f_regmap[hr])<0) { + if(regs[t].regmap[hr]==f_regmap[hr]||(regs[t].regmap_entry[hr]<0&&get_reg(regmap_pre[t],f_regmap[hr])<0)) { int r=f_regmap[hr]; for(j=t;j<=i;j++) { @@ -9992,7 +9992,7 @@ int new_recompile_block(int addr) break; } // call/ret fast path assumes no registers allocated - if(k>2&&(itype[k-3]==UJUMP||itype[k-3]==RJUMP)) { + if(k>2&&(itype[k-3]==UJUMP||itype[k-3]==RJUMP)&&rt1[k-3]==31) { break; } if(r>63) { @@ -10140,7 +10140,7 @@ int new_recompile_block(int addr) } } }else{ - int count=0; + // Non branch or undetermined branch target for(hr=0;hr>2; if(t=earliest_available[hr]) { - // Score a point for hoisting loop invariant - if(t1&&itype[t-2]!=UJUMP&&itype[t-2]!=RJUMP)||(t>1&&rt1[t-2]!=31)) { // call/ret assumes no registers allocated + // Score a point for hoisting loop invariant + if(t>2; - if(t==loop_start[maxscore]) regs[t].regmap_entry[maxscore]=reg; + if(t==loop_start[maxscore]) { + if(t==1||(t>1&&itype[t-2]!=UJUMP&&itype[t-2]!=RJUMP)||(t>1&&rt1[t-2]!=31)) // call/ret assumes no registers allocated + regs[t].regmap_entry[maxscore]=reg; + } } else { @@ -10439,6 +10450,7 @@ int new_recompile_block(int addr) } } } + // Preload target address for load instruction (non-constant) if(itype[i+1]==LOAD&&rs1[i+1]&&get_reg(regs[i+1].regmap,rs1[i+1])<0) { if((hr=get_reg(regs[i+1].regmap,rt1[i+1]))>=0) { @@ -10455,6 +10467,7 @@ int new_recompile_block(int addr) } } } + // Load source into target register if(lt1[i+1]&&get_reg(regs[i+1].regmap,rs1[i+1])<0) { if((hr=get_reg(regs[i+1].regmap,rt1[i+1]))>=0) { @@ -10471,6 +10484,7 @@ int new_recompile_block(int addr) } } } + // Preload map address #ifndef HOST_IMM_ADDR32 if(itype[i+1]==LOAD||itype[i+1]==LOADLR||itype[i+1]==STORE||itype[i+1]==STORELR||itype[i+1]==C1LS||itype[i+1]==C2LS) { hr=get_reg(regs[i+1].regmap,TLREG); @@ -10510,6 +10524,7 @@ int new_recompile_block(int addr) } } #endif + // Address for store instruction (non-constant) if(itype[i+1]==STORE||itype[i+1]==STORELR ||(opcode[i+1]&0x3b)==0x39||(opcode[i+1]&0x3b)==0x3a) { // SB/SH/SW/SD/SWC1/SDC1/SWC2/SDC2 if(get_reg(regs[i+1].regmap,rs1[i+1])<0) { -- 2.39.2 From 5a05d80c2ed15a50915dc7f820155893c4735e01 Mon Sep 17 00:00:00 2001 From: notaz Date: Wed, 22 Jun 2011 18:54:26 +0300 Subject: [PATCH 16/16] drc: merge Ari64's patch: 19_arm_typos --- libpcsxcore/new_dynarec/assem_arm.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/libpcsxcore/new_dynarec/assem_arm.c b/libpcsxcore/new_dynarec/assem_arm.c index af45ce37..5ee226b0 100644 --- a/libpcsxcore/new_dynarec/assem_arm.c +++ b/libpcsxcore/new_dynarec/assem_arm.c @@ -1057,7 +1057,7 @@ void emit_test(int rs, int rt) void emit_testimm(int rs,int imm) { u_int armval; - assem_debug("tst %s,$%d\n",regname[rs],imm); + assem_debug("tst %s,#%d\n",regname[rs],imm); genimm_checked(imm,&armval); output_w32(0xe3100000|rd_rn_rm(0,rs,0)|armval); } @@ -1450,10 +1450,10 @@ void emit_cmpimm(int rs,int imm) { u_int armval; if(genimm(imm,&armval)) { - assem_debug("cmp %s,$%d\n",regname[rs],imm); + assem_debug("cmp %s,#%d\n",regname[rs],imm); output_w32(0xe3500000|rd_rn_rm(0,rs,0)|armval); }else if(genimm(-imm,&armval)) { - assem_debug("cmn %s,$%d\n",regname[rs],imm); + assem_debug("cmn %s,#%d\n",regname[rs],imm); output_w32(0xe3700000|rd_rn_rm(0,rs,0)|armval); }else if(imm>0) { assert(imm<65536); -- 2.39.2