+static bool remove_div_sequence(struct block *block, unsigned int offset)
+{
+ struct opcode *op;
+ unsigned int i, found = 0;
+
+ /*
+ * Scan for the zero-checking sequence that GCC automatically introduced
+ * after most DIV/DIVU opcodes. This sequence checks the value of the
+ * divisor, and if zero, executes a BREAK opcode, causing the BIOS
+ * handler to crash the PS1.
+ *
+ * For DIV opcodes, this sequence additionally checks that the signed
+ * operation does not overflow.
+ *
+ * With the assumption that the games never crashed the PS1, we can
+ * therefore assume that the games never divided by zero or overflowed,
+ * and these sequences can be removed.
+ */
+
+ for (i = offset; i < block->nb_ops; i++) {
+ op = &block->opcode_list[i];
+
+ if (!found) {
+ if (op->i.op == OP_SPECIAL &&
+ (op->r.op == OP_SPECIAL_DIV || op->r.op == OP_SPECIAL_DIVU))
+ break;
+
+ if ((op->opcode & 0xfc1fffff) == 0x14000002) {
+ /* BNE ???, zero, +8 */
+ found++;
+ } else {
+ offset++;
+ }
+ } else if (found == 1 && !op->opcode) {
+ /* NOP */
+ found++;
+ } else if (found == 2 && op->opcode == 0x0007000d) {
+ /* BREAK 0x1c00 */
+ found++;
+ } else if (found == 3 && op->opcode == 0x2401ffff) {
+ /* LI at, -1 */
+ found++;
+ } else if (found == 4 && (op->opcode & 0xfc1fffff) == 0x14010004) {
+ /* BNE ???, at, +16 */
+ found++;
+ } else if (found == 5 && op->opcode == 0x3c018000) {
+ /* LUI at, 0x8000 */
+ found++;
+ } else if (found == 6 && (op->opcode & 0x141fffff) == 0x14010002) {
+ /* BNE ???, at, +16 */
+ found++;
+ } else if (found == 7 && !op->opcode) {
+ /* NOP */
+ found++;
+ } else if (found == 8 && op->opcode == 0x0006000d) {
+ /* BREAK 0x1800 */
+ found++;
+ break;
+ } else {
+ break;
+ }
+ }
+
+ if (found >= 3) {
+ if (found != 9)
+ found = 3;
+
+ pr_debug("Removing DIV%s sequence at offset 0x%x\n",
+ found == 9 ? "" : "U", offset << 2);
+
+ for (i = 0; i < found; i++)
+ block->opcode_list[offset + i].opcode = 0;
+
+ return true;
+ }
+
+ return false;
+}
+
+static int lightrec_remove_div_by_zero_check_sequence(struct lightrec_state *state,
+ struct block *block)
+{
+ struct opcode *op;
+ unsigned int i;
+
+ for (i = 0; i < block->nb_ops; i++) {
+ op = &block->opcode_list[i];
+
+ if (op->i.op == OP_SPECIAL &&
+ (op->r.op == OP_SPECIAL_DIVU || op->r.op == OP_SPECIAL_DIV) &&
+ remove_div_sequence(block, i + 1))
+ op->flags |= LIGHTREC_NO_DIV_CHECK;
+ }
+
+ return 0;
+}
+
+static const u32 memset_code[] = {
+ 0x10a00006, // beqz a1, 2f
+ 0x24a2ffff, // addiu v0,a1,-1
+ 0x2403ffff, // li v1,-1
+ 0xac800000, // 1: sw zero,0(a0)
+ 0x2442ffff, // addiu v0,v0,-1
+ 0x1443fffd, // bne v0,v1, 1b
+ 0x24840004, // addiu a0,a0,4
+ 0x03e00008, // 2: jr ra
+ 0x00000000, // nop