Path: blob/master/libmupen64plus/mupen64plus-rsp-hle/src/jpeg.c
2 views
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *1* Mupen64plus-rsp-hle - jpeg.c *2* Mupen64Plus homepage: http://code.google.com/p/mupen64plus/ *3* Copyright (C) 2012 Bobby Smiles *4* Copyright (C) 2009 Richard Goedeken *5* Copyright (C) 2002 Hacktarux *6* *7* This program is free software; you can redistribute it and/or modify *8* it under the terms of the GNU General Public License as published by *9* the Free Software Foundation; either version 2 of the License, or *10* (at your option) any later version. *11* *12* This program is distributed in the hope that it will be useful, *13* but WITHOUT ANY WARRANTY; without even the implied warranty of *14* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *15* GNU General Public License for more details. *16* *17* You should have received a copy of the GNU General Public License *18* along with this program; if not, write to the *19* Free Software Foundation, Inc., *20* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *21* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */2223#include <assert.h>24#include <stdlib.h>25#include <stdint.h>2627#define M64P_PLUGIN_PROTOTYPES 128#include "m64p_types.h"29#include "m64p_plugin.h"30#include "hle.h"3132#define SUBBLOCK_SIZE 643334typedef void (*tile_line_emitter_t)(const int16_t *y, const int16_t *u, uint32_t address);35typedef void (*std_macroblock_decoder_t)(int16_t *macroblock, unsigned int subblock_count, const int16_t qtables[3][SUBBLOCK_SIZE]);3637/* rdram operations */38// FIXME: these functions deserve their own module39static void rdram_read_many_u16(uint16_t *dst, uint32_t address, unsigned int count);40static void rdram_write_many_u16(const uint16_t *src, uint32_t address, unsigned int count);41static uint32_t rdram_read_u32(uint32_t address);42static void rdram_write_many_u32(const uint32_t *src, uint32_t address, unsigned int count);4344/* standard jpeg ucode decoder */45static void jpeg_decode_std(const char * const version, const std_macroblock_decoder_t decode_mb, const tile_line_emitter_t emit_line);4647/* helper functions */48static uint8_t clamp_u8(int16_t x);49static int16_t clamp_s12(int16_t x);50static int16_t clamp_s16(int32_t x);51static uint16_t clamp_RGBA_component(int16_t x);5253/* pixel conversion & foratting */54static uint32_t GetUYVY(int16_t y1, int16_t y2, int16_t u, int16_t v);55static uint16_t GetRGBA(int16_t y, int16_t u, int16_t v);5657/* tile line emitters */58static void EmitYUVTileLine(const int16_t *y, const int16_t *u, uint32_t address);59static void EmitRGBATileLine(const int16_t *y, const int16_t *u, uint32_t address);6061/* macroblocks operations */62static void DecodeMacroblock1(int16_t *macroblock, int32_t *y_dc, int32_t *u_dc, int32_t *v_dc, const int16_t *qtable);63static void DecodeMacroblock2(int16_t *macroblock, unsigned int subblock_count, const int16_t qtables[3][SUBBLOCK_SIZE]);64static void DecodeMacroblock3(int16_t *macroblock, unsigned int subblock_count, const int16_t qtables[3][SUBBLOCK_SIZE]);65static void EmitTilesMode0(const tile_line_emitter_t emit_line, const int16_t *macroblock, uint32_t address);66static void EmitTilesMode2(const tile_line_emitter_t emit_line, const int16_t *macroblock, uint32_t address);6768/* subblocks operations */69static void TransposeSubBlock(int16_t *dst, const int16_t *src);70static void ZigZagSubBlock(int16_t *dst, const int16_t *src);71static void ReorderSubBlock(int16_t *dst, const int16_t *src, const unsigned int *table);72static void MultSubBlocks(int16_t *dst, const int16_t *src1, const int16_t *src2, unsigned int shift);73static void ScaleSubBlock(int16_t *dst, const int16_t *src, int16_t scale);74static void RShiftSubBlock(int16_t *dst, const int16_t *src, unsigned int shift);75static void InverseDCT1D(const float * const x, float *dst, unsigned int stride);76static void InverseDCTSubBlock(int16_t *dst, const int16_t *src);77static void RescaleYSubBlock(int16_t *dst, const int16_t *src);78static void RescaleUVSubBlock(int16_t *dst, const int16_t *src);7980/* transposed dequantization table */81static const int16_t DEFAULT_QTABLE[SUBBLOCK_SIZE] =82{8316, 12, 14, 14, 18, 24, 49, 72,8411, 12, 13, 17, 22, 35, 64, 92,8510, 14, 16, 22, 37, 55, 78, 95,8616, 19, 24, 29, 56, 64, 87, 98,8724, 26, 40, 51, 68, 81, 103, 112,8840, 58, 57, 87, 109, 104, 121, 100,8951, 60, 69, 80, 103, 113, 120, 103,9061, 55, 56, 62, 77, 92, 101, 9991};9293/* zig-zag indices */94static const unsigned int ZIGZAG_TABLE[SUBBLOCK_SIZE] =95{960, 1, 5, 6, 14, 15, 27, 28,972, 4, 7, 13, 16, 26, 29, 42,983, 8, 12, 17, 25, 30, 41, 43,999, 11, 18, 24, 31, 40, 44, 53,10010, 19, 23, 32, 39, 45, 52, 54,10120, 22, 33, 38, 46, 51, 55, 60,10221, 34, 37, 47, 50, 56, 59, 61,10335, 36, 48, 49, 57, 58, 62, 63104};105106/* transposition indices */107static const unsigned int TRANSPOSE_TABLE[SUBBLOCK_SIZE] =108{1090, 8, 16, 24, 32, 40, 48, 56,1101, 9, 17, 25, 33, 41, 49, 57,1112, 10, 18, 26, 34, 42, 50, 58,1123, 11, 19, 27, 35, 43, 51, 59,1134, 12, 20, 28, 36, 44, 52, 60,1145, 13, 21, 29, 37, 45, 53, 61,1156, 14, 22, 30, 38, 46, 54, 62,1167, 15, 23, 31, 39, 47, 55, 63117};118119120121/* IDCT related constants122* Cn = alpha * cos(n * PI / 16) (alpha is chosen such as C4 = 1) */123static const float IDCT_C3 = 1.175875602f;124static const float IDCT_C6 = 0.541196100f;125static const float IDCT_K[10] =126{1270.765366865f, /* C2-C6 */128-1.847759065f, /* -C2-C6 */129-0.390180644f, /* C5-C3 */130-1.961570561f, /* -C5-C3 */1311.501321110f, /* C1+C3-C5-C7 */1322.053119869f, /* C1+C3-C5+C7 */1333.072711027f, /* C1+C3+C5-C7 */1340.298631336f, /* -C1+C3+C5-C7 */135-0.899976223f, /* C7-C3 */136-2.562915448f /* -C1-C3 */137};138139140/* global functions */141142/***************************************************************************143* JPEG decoding ucode found in Japanese exclusive version of Pokemon Stadium.144**************************************************************************/145void jpeg_decode_PS0()146{147jpeg_decode_std("PS0", DecodeMacroblock3, EmitYUVTileLine);148}149150/***************************************************************************151* JPEG decoding ucode found in Ocarina of Time, Pokemon Stadium 1 and152* Pokemon Stadium 2.153**************************************************************************/154void jpeg_decode_PS()155{156jpeg_decode_std("PS", DecodeMacroblock2, EmitRGBATileLine);157}158159/***************************************************************************160* JPEG decoding ucode found in Ogre Battle and Bottom of the 9th.161**************************************************************************/162void jpeg_decode_OB()163{164int16_t qtable[SUBBLOCK_SIZE];165unsigned int mb;166167int32_t y_dc = 0;168int32_t u_dc = 0;169int32_t v_dc = 0;170171const OSTask_t * const task = get_task();172173uint32_t address = task->data_ptr;174const unsigned int macroblock_count = task->data_size;175const int qscale = task->yield_data_size;176177DebugMessage(M64MSG_VERBOSE, "jpeg_decode_OB: *buffer=%x, #MB=%d, qscale=%d",178address,179macroblock_count,180qscale);181182if (qscale != 0)183{184if (qscale > 0)185{186ScaleSubBlock(qtable, DEFAULT_QTABLE, qscale);187}188else189{190RShiftSubBlock(qtable, DEFAULT_QTABLE, -qscale);191}192}193194for (mb = 0; mb < macroblock_count; ++mb)195{196int16_t macroblock[6*SUBBLOCK_SIZE];197198rdram_read_many_u16((uint16_t*)macroblock, address, 6*SUBBLOCK_SIZE);199DecodeMacroblock1(macroblock, &y_dc, &u_dc, &v_dc, (qscale != 0) ? qtable : NULL);200EmitTilesMode2(EmitYUVTileLine, macroblock, address);201202address += (2*6*SUBBLOCK_SIZE);203}204}205206207/* local functions */208static void jpeg_decode_std(const char * const version, const std_macroblock_decoder_t decode_mb, const tile_line_emitter_t emit_line)209{210int16_t qtables[3][SUBBLOCK_SIZE];211unsigned int mb;212uint32_t address;213uint32_t macroblock_count;214uint32_t mode;215uint32_t qtableY_ptr;216uint32_t qtableU_ptr;217uint32_t qtableV_ptr;218unsigned int subblock_count;219unsigned int macroblock_size;220int16_t *macroblock;221const OSTask_t * const task = get_task();222223if (task->flags & 0x1)224{225DebugMessage(M64MSG_WARNING, "jpeg_decode_%s: task yielding not implemented", version);226return;227}228229address = rdram_read_u32(task->data_ptr);230macroblock_count = rdram_read_u32(task->data_ptr + 4);231mode = rdram_read_u32(task->data_ptr + 8);232qtableY_ptr = rdram_read_u32(task->data_ptr + 12);233qtableU_ptr = rdram_read_u32(task->data_ptr + 16);234qtableV_ptr = rdram_read_u32(task->data_ptr + 20);235236DebugMessage(M64MSG_VERBOSE, "jpeg_decode_%s: *buffer=%x, #MB=%d, mode=%d, *Qy=%x, *Qu=%x, *Qv=%x",237version,238address,239macroblock_count,240mode,241qtableY_ptr,242qtableU_ptr,243qtableV_ptr);244245if (mode != 0 && mode != 2)246{247DebugMessage(M64MSG_WARNING, "jpeg_decode_%s: invalid mode %d", version, mode);248return;249}250251subblock_count = mode + 4;252macroblock_size = 2*subblock_count*SUBBLOCK_SIZE;253254rdram_read_many_u16((uint16_t*)qtables[0], qtableY_ptr, SUBBLOCK_SIZE);255rdram_read_many_u16((uint16_t*)qtables[1], qtableU_ptr, SUBBLOCK_SIZE);256rdram_read_many_u16((uint16_t*)qtables[2], qtableV_ptr, SUBBLOCK_SIZE);257258macroblock = malloc(sizeof(*macroblock) * macroblock_size);259if (!macroblock)260{261DebugMessage(M64MSG_WARNING, "jpeg_decode_%s: could not allocate macroblock", version);262return;263}264265for (mb = 0; mb < macroblock_count; ++mb)266{267rdram_read_many_u16((uint16_t*)macroblock, address, macroblock_size >> 1);268decode_mb(macroblock, subblock_count, (const int16_t (*)[SUBBLOCK_SIZE])qtables);269270if (mode == 0)271{272EmitTilesMode0(emit_line, macroblock, address);273}274else275{276EmitTilesMode2(emit_line, macroblock, address);277}278279address += macroblock_size;280}281free(macroblock);282}283284static uint8_t clamp_u8(int16_t x)285{286return (x & (0xff00)) ? ((-x) >> 15) & 0xff : x;287}288289static int16_t clamp_s12(int16_t x)290{291if (x < -0x800) { x = -0x800; } else if (x > 0x7f0) { x = 0x7f0; }292return x;293}294295static int16_t clamp_s16(int32_t x)296{297if (x > 32767) { x = 32767; } else if (x < -32768) { x = -32768; }298return x;299}300301static uint16_t clamp_RGBA_component(int16_t x)302{303if (x > 0xff0) { x = 0xff0; } else if (x < 0) { x = 0; }304return (x & 0xf80);305}306307static uint32_t GetUYVY(int16_t y1, int16_t y2, int16_t u, int16_t v)308{309return (uint32_t)clamp_u8(u) << 24310| (uint32_t)clamp_u8(y1) << 16311| (uint32_t)clamp_u8(v) << 8312| (uint32_t)clamp_u8(y2);313}314315static uint16_t GetRGBA(int16_t y, int16_t u, int16_t v)316{317const float fY = (float)y + 2048.0f;318const float fU = (float)u;319const float fV = (float)v;320321const uint16_t r = clamp_RGBA_component((int16_t)(fY + 1.4025*fV));322const uint16_t g = clamp_RGBA_component((int16_t)(fY - 0.3443*fU - 0.7144*fV));323const uint16_t b = clamp_RGBA_component((int16_t)(fY + 1.7729*fU ));324325return (r << 4) | (g >> 1) | (b >> 6) | 1;326}327328static void EmitYUVTileLine(const int16_t *y, const int16_t *u, uint32_t address)329{330uint32_t uyvy[8];331332const int16_t * const v = u + SUBBLOCK_SIZE;333const int16_t * const y2 = y + SUBBLOCK_SIZE;334335uyvy[0] = GetUYVY(y[0], y[1], u[0], v[0]);336uyvy[1] = GetUYVY(y[2], y[3], u[1], v[1]);337uyvy[2] = GetUYVY(y[4], y[5], u[2], v[2]);338uyvy[3] = GetUYVY(y[6], y[7], u[3], v[3]);339uyvy[4] = GetUYVY(y2[0], y2[1], u[4], v[4]);340uyvy[5] = GetUYVY(y2[2], y2[3], u[5], v[5]);341uyvy[6] = GetUYVY(y2[4], y2[5], u[6], v[6]);342uyvy[7] = GetUYVY(y2[6], y2[7], u[7], v[7]);343344rdram_write_many_u32(uyvy, address, 8);345}346347static void EmitRGBATileLine(const int16_t *y, const int16_t *u, uint32_t address)348{349uint16_t rgba[16];350351const int16_t * const v = u + SUBBLOCK_SIZE;352const int16_t * const y2 = y + SUBBLOCK_SIZE;353354rgba[0] = GetRGBA(y[0], u[0], v[0]);355rgba[1] = GetRGBA(y[1], u[0], v[0]);356rgba[2] = GetRGBA(y[2], u[1], v[1]);357rgba[3] = GetRGBA(y[3], u[1], v[1]);358rgba[4] = GetRGBA(y[4], u[2], v[2]);359rgba[5] = GetRGBA(y[5], u[2], v[2]);360rgba[6] = GetRGBA(y[6], u[3], v[3]);361rgba[7] = GetRGBA(y[7], u[3], v[3]);362rgba[8] = GetRGBA(y2[0], u[4], v[4]);363rgba[9] = GetRGBA(y2[1], u[4], v[4]);364rgba[10] = GetRGBA(y2[2], u[5], v[5]);365rgba[11] = GetRGBA(y2[3], u[5], v[5]);366rgba[12] = GetRGBA(y2[4], u[6], v[6]);367rgba[13] = GetRGBA(y2[5], u[6], v[6]);368rgba[14] = GetRGBA(y2[6], u[7], v[7]);369rgba[15] = GetRGBA(y2[7], u[7], v[7]);370371rdram_write_many_u16(rgba, address, 16);372}373374static void EmitTilesMode0(const tile_line_emitter_t emit_line, const int16_t *macroblock, uint32_t address)375{376unsigned int i;377378unsigned int y_offset = 0;379unsigned int u_offset = 2*SUBBLOCK_SIZE;380381for (i = 0; i < 8; ++i)382{383emit_line(¯oblock[y_offset], ¯oblock[u_offset], address);384385y_offset += 8;386u_offset += 8;387address += 32;388}389}390391static void EmitTilesMode2(const tile_line_emitter_t emit_line, const int16_t *macroblock, uint32_t address)392{393unsigned int i;394395unsigned int y_offset = 0;396unsigned int u_offset = 4*SUBBLOCK_SIZE;397398for (i = 0; i < 8; ++i)399{400emit_line(¯oblock[y_offset], ¯oblock[u_offset], address);401emit_line(¯oblock[y_offset + 8], ¯oblock[u_offset], address + 32);402403y_offset += (i == 3) ? SUBBLOCK_SIZE+16 : 16;404u_offset += 8;405address += 64;406}407}408409static void DecodeMacroblock1(int16_t *macroblock, int32_t *y_dc, int32_t *u_dc, int32_t *v_dc, const int16_t *qtable)410{411int sb;412413for (sb = 0; sb < 6; ++sb)414{415int16_t tmp_sb[SUBBLOCK_SIZE];416417/* update DC */418int32_t dc = (int32_t)macroblock[0];419switch(sb)420{421case 0: case 1: case 2: case 3:422*y_dc += dc; macroblock[0] = *y_dc & 0xffff; break;423case 4: *u_dc += dc; macroblock[0] = *u_dc & 0xffff; break;424case 5: *v_dc += dc; macroblock[0] = *v_dc & 0xffff; break;425}426427ZigZagSubBlock(tmp_sb, macroblock);428if (qtable != NULL) { MultSubBlocks(tmp_sb, tmp_sb, qtable, 0); }429TransposeSubBlock(macroblock, tmp_sb);430InverseDCTSubBlock(macroblock, macroblock);431432macroblock += SUBBLOCK_SIZE;433}434}435436static void DecodeMacroblock2(int16_t *macroblock, unsigned int subblock_count, const int16_t qtables[3][SUBBLOCK_SIZE])437{438unsigned int sb;439unsigned int q = 0;440441for (sb = 0; sb < subblock_count; ++sb)442{443int16_t tmp_sb[SUBBLOCK_SIZE];444const int isChromaSubBlock = (subblock_count - sb <= 2);445446if (isChromaSubBlock) { ++q; }447448MultSubBlocks(macroblock, macroblock, qtables[q], 4);449ZigZagSubBlock(tmp_sb, macroblock);450InverseDCTSubBlock(macroblock, tmp_sb);451452macroblock += SUBBLOCK_SIZE;453}454455}456457static void DecodeMacroblock3(int16_t *macroblock, unsigned int subblock_count, const int16_t qtables[3][SUBBLOCK_SIZE])458{459unsigned int sb;460unsigned int q = 0;461462for (sb = 0; sb < subblock_count; ++sb)463{464int16_t tmp_sb[SUBBLOCK_SIZE];465const int isChromaSubBlock = (subblock_count - sb <= 2);466467if (isChromaSubBlock) { ++q; }468469MultSubBlocks(macroblock, macroblock, qtables[q], 4);470ZigZagSubBlock(tmp_sb, macroblock);471InverseDCTSubBlock(macroblock, tmp_sb);472473if (isChromaSubBlock)474{475RescaleUVSubBlock(macroblock, macroblock);476}477else478{479RescaleYSubBlock(macroblock, macroblock);480}481482macroblock += SUBBLOCK_SIZE;483}484}485486static void TransposeSubBlock(int16_t *dst, const int16_t *src)487{488ReorderSubBlock(dst, src, TRANSPOSE_TABLE);489}490491static void ZigZagSubBlock(int16_t *dst, const int16_t *src)492{493ReorderSubBlock(dst, src, ZIGZAG_TABLE);494}495496static void ReorderSubBlock(int16_t *dst, const int16_t *src, const unsigned int *table)497{498unsigned int i;499500/* source and destination sublocks cannot overlap */501assert(abs(dst - src) > SUBBLOCK_SIZE);502503for (i = 0; i < SUBBLOCK_SIZE; ++i)504{505dst[i] = src[table[i]];506}507}508509static void MultSubBlocks(int16_t *dst, const int16_t *src1, const int16_t *src2, unsigned int shift)510{511unsigned int i;512513for (i = 0; i < SUBBLOCK_SIZE; ++i)514{515int32_t v = src1[i] * src2[i];516dst[i] = clamp_s16(v) << shift;517}518}519520static void ScaleSubBlock(int16_t *dst, const int16_t *src, int16_t scale)521{522unsigned int i;523524for (i = 0; i < SUBBLOCK_SIZE; ++i)525{526int32_t v = src[i] * scale;527dst[i] = clamp_s16(v);528}529}530531static void RShiftSubBlock(int16_t *dst, const int16_t *src, unsigned int shift)532{533unsigned int i;534535for (i = 0; i < SUBBLOCK_SIZE; ++i)536{537dst[i] = src[i] >> shift;538}539}540541/***************************************************************************542* Fast 2D IDCT using separable formulation and normalization543* Computations use single precision floats544* Implementation based on Wikipedia :545* http://fr.wikipedia.org/wiki/Transform%C3%A9e_en_cosinus_discr%C3%A8te546**************************************************************************/547static void InverseDCT1D(const float * const x, float *dst, unsigned int stride)548{549float e[4];550float f[4];551float x26, x1357, x15, x37, x17, x35;552553x15 = IDCT_K[2] * (x[1] + x[5]);554x37 = IDCT_K[3] * (x[3] + x[7]);555x17 = IDCT_K[8] * (x[1] + x[7]);556x35 = IDCT_K[9] * (x[3] + x[5]);557x1357 = IDCT_C3 * (x[1] + x[3] + x[5] + x[7]);558x26 = IDCT_C6 * (x[2] + x[6]);559560f[0] = x[0] + x[4];561f[1] = x[0] - x[4];562f[2] = x26 + IDCT_K[0]*x[2];563f[3] = x26 + IDCT_K[1]*x[6];564565e[0] = x1357 + x15 + IDCT_K[4]*x[1] + x17;566e[1] = x1357 + x37 + IDCT_K[6]*x[3] + x35;567e[2] = x1357 + x15 + IDCT_K[5]*x[5] + x35;568e[3] = x1357 + x37 + IDCT_K[7]*x[7] + x17;569570*dst = f[0] + f[2] + e[0]; dst += stride;571*dst = f[1] + f[3] + e[1]; dst += stride;572*dst = f[1] - f[3] + e[2]; dst += stride;573*dst = f[0] - f[2] + e[3]; dst += stride;574*dst = f[0] - f[2] - e[3]; dst += stride;575*dst = f[1] - f[3] - e[2]; dst += stride;576*dst = f[1] + f[3] - e[1]; dst += stride;577*dst = f[0] + f[2] - e[0]; dst += stride;578}579580static void InverseDCTSubBlock(int16_t *dst, const int16_t *src)581{582float x[8];583float block[SUBBLOCK_SIZE];584unsigned int i, j;585586/* idct 1d on rows (+transposition) */587for (i = 0; i < 8; ++i)588{589for (j = 0; j < 8; ++j)590{591x[j] = (float)src[i*8+j];592}593594InverseDCT1D(x, &block[i], 8);595}596597/* idct 1d on columns (thanks to previous transposition) */598for (i = 0; i < 8; ++i)599{600InverseDCT1D(&block[i*8], x, 1);601602/* C4 = 1 normalization implies a division by 8 */603for (j = 0; j < 8; ++j)604{605dst[i+j*8] = (int16_t)x[j] >> 3;606}607}608}609610static void RescaleYSubBlock(int16_t *dst, const int16_t *src)611{612unsigned int i;613614for (i = 0; i < SUBBLOCK_SIZE; ++i)615{616dst[i] = (((uint32_t)(clamp_s12(src[i]) + 0x800) * 0xdb0) >> 16) + 0x10;617}618}619620static void RescaleUVSubBlock(int16_t *dst, const int16_t *src)621{622unsigned int i;623624for (i = 0; i < SUBBLOCK_SIZE; ++i)625{626dst[i] = (((int)clamp_s12(src[i]) * 0xe00) >> 16) + 0x80;627}628}629630631632/* FIXME: assume presence of expansion pack */633#define MEMMASK 0x7fffff634635static void rdram_read_many_u16(uint16_t *dst, uint32_t address, unsigned int count)636{637while (count != 0)638{639uint16_t s = rsp.RDRAM[((address++)^S8) & MEMMASK];640s <<= 8;641s |= rsp.RDRAM[((address++)^S8) & MEMMASK];642643*(dst++) = s;644645--count;646}647}648649static void rdram_write_many_u16(const uint16_t *src, uint32_t address, unsigned int count)650{651while (count != 0)652{653rsp.RDRAM[((address++)^S8) & MEMMASK] = (uint8_t)(*src >> 8);654rsp.RDRAM[((address++)^S8) & MEMMASK] = (uint8_t)(*(src++) & 0xff);655656--count;657}658}659660static uint32_t rdram_read_u32(uint32_t address)661{662uint32_t r = rsp.RDRAM[((address++) ^ S8) & MEMMASK]; r <<= 8;663r |= rsp.RDRAM[((address++) ^ S8) & MEMMASK]; r <<= 8;664r |= rsp.RDRAM[((address++) ^ S8) & MEMMASK]; r <<= 8;665r |= rsp.RDRAM[((address++) ^ S8) & MEMMASK];666667return r;668}669670static void rdram_write_many_u32(const uint32_t *src, uint32_t address, unsigned int count)671{672while (count != 0)673{674rsp.RDRAM[((address++)^S8) & MEMMASK] = (uint8_t)(*src >> 24);675rsp.RDRAM[((address++)^S8) & MEMMASK] = (uint8_t)(*src >> 16);676rsp.RDRAM[((address++)^S8) & MEMMASK] = (uint8_t)(*src >> 8);677rsp.RDRAM[((address++)^S8) & MEMMASK] = (uint8_t)(*(src++) & 0xff);678679--count;680}681}682683684685