goldfish_fb: port to modern qemu apis

The device/object APIs and display APIs have been significantly
refactored.  qemu also has a tracing API which can replace the debugging
printfs.

Signed-off-by: Greg Hackmann <ghackmann@google.com>
diff --git a/default-configs/arm-softmmu.mak b/default-configs/arm-softmmu.mak
index f3513fa..b1dc85f 100644
--- a/default-configs/arm-softmmu.mak
+++ b/default-configs/arm-softmmu.mak
@@ -78,6 +78,7 @@
 CONFIG_REALVIEW=y
 CONFIG_ZAURUS=y
 CONFIG_ZYNQ=y
+CONFIG_GOLDFISH=y
 
 CONFIG_VERSATILE_PCI=y
 CONFIG_VERSATILE_I2C=y
diff --git a/hw/display/Makefile.objs b/hw/display/Makefile.objs
index 7ed76a9..2456da6 100644
--- a/hw/display/Makefile.objs
+++ b/hw/display/Makefile.objs
@@ -15,6 +15,7 @@
 common-obj-$(CONFIG_BLIZZARD) += blizzard.o
 common-obj-$(CONFIG_EXYNOS4) += exynos4210_fimd.o
 common-obj-$(CONFIG_FRAMEBUFFER) += framebuffer.o
+common-obj-$(CONFIG_GOLDFISH) += goldfish_fb.o
 common-obj-$(CONFIG_MILKYMIST) += milkymist-vgafb.o
 common-obj-$(CONFIG_ZAURUS) += tc6393xb.o
 
diff --git a/hw/display/goldfish_fb.c b/hw/display/goldfish_fb.c
index b2ac750..66fdb21 100644
--- a/hw/display/goldfish_fb.c
+++ b/hw/display/goldfish_fb.c
@@ -1,4 +1,4 @@
-/* Copyright (C) 2007-2008 The Android Open Source Project
+/* Copyright (C) 2007-2013 The Android Open Source Project
 **
 ** This software is licensed under the terms of the GNU General Public
 ** License version 2, as published by the Free Software Foundation, and
@@ -9,12 +9,27 @@
 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 ** GNU General Public License for more details.
 */
-#include "qemu_file.h"
-#include "android/android.h"
-#include "android/utils/debug.h"
-#include "android/utils/duff.h"
-#include "goldfish_device.h"
-#include "console.h"
+#include "framebuffer.h"
+#include "hw/hw.h"
+#include "hw/sysbus.h"
+#include "ui/console.h"
+#include "ui/pixel_ops.h"
+#include "trace.h"
+
+/* milkymist-vgafb also uses RGB565, so borrow its drawfn helpers */
+#define BITS 8
+#include "milkymist-vgafb_template.h"
+#define BITS 15
+#include "milkymist-vgafb_template.h"
+#define BITS 16
+#include "milkymist-vgafb_template.h"
+#define BITS 24
+#include "milkymist-vgafb_template.h"
+#define BITS 32
+#include "milkymist-vgafb_template.h"
+
+#define TYPE_GOLDFISH_FB "goldfish_fb"
+#define GOLDFISH_FB(obj) OBJECT_CHECK(struct goldfish_fb_state, (obj), TYPE_GOLDFISH_FB)
 
 enum {
     FB_GET_WIDTH        = 0x00,
@@ -32,8 +47,12 @@
 };
 
 struct goldfish_fb_state {
-    struct goldfish_device dev;
-    DisplayState*  ds;
+    SysBusDevice parent;
+
+    QemuConsole *con;
+    MemoryRegion iomem;
+    qemu_irq irq;
+
     uint32_t fb_base;
     uint32_t base_valid : 1;
     uint32_t need_update : 1;
@@ -52,11 +71,11 @@
 {
     struct goldfish_fb_state*  s = opaque;
 
-    DisplayState*  ds = s->ds;
+    DisplaySurface *ds = qemu_console_surface(s->con);
 
-    qemu_put_be32(f, ds->surface->width);
-    qemu_put_be32(f, ds->surface->height);
-    qemu_put_be32(f, ds->surface->linesize);
+    qemu_put_be32(f, surface_width(ds));
+    qemu_put_be32(f, surface_height(ds));
+    qemu_put_be32(f, surface_stride(ds));
     qemu_put_byte(f, 0);
 
     qemu_put_be32(f, s->fb_base);
@@ -85,11 +104,11 @@
     ds_pitch = qemu_get_be32(f);
     ds_rot   = qemu_get_byte(f);
 
-    DisplayState*  ds = s->ds;
+    DisplaySurface *ds = qemu_console_surface(s->con);
 
-    if (ds->surface->width != ds_w ||
-        ds->surface->height != ds_h ||
-        ds->surface->linesize != ds_pitch ||
+    if (surface_width(ds) != ds_w ||
+        surface_height(ds) != ds_h ||
+        surface_stride(ds) != ds_pitch ||
         ds_rot != 0)
     {
         /* XXX: We should be able to force a resize/rotation from here ? */
@@ -137,239 +156,19 @@
 static long  stats_total_full_updates;
 #endif
 
-/* This structure is used to hold the inputs for
- * compute_fb_update_rect_linear below.
- * This corresponds to the source framebuffer and destination
- * surface pixel buffers.
- */
-typedef struct {
-    int            width;
-    int            height;
-    int            bytes_per_pixel;
-    const uint8_t* src_pixels;
-    int            src_pitch;
-    uint8_t*       dst_pixels;
-    int            dst_pitch;
-} FbUpdateState;
-
-/* This structure is used to hold the outputs for
- * compute_fb_update_rect_linear below.
- * This corresponds to the smalled bounding rectangle of the
- * latest framebuffer update.
- */
-typedef struct {
-    int xmin, ymin, xmax, ymax;
-} FbUpdateRect;
-
-/* Determine the smallest bounding rectangle of pixels which changed
- * between the source (framebuffer) and destination (surface) pixel
- * buffers.
- *
- * Return 0 if there was no change, otherwise, populate '*rect'
- * and return 1.
- *
- * If 'dirty_base' is not 0, it is a physical address that will be
- * used to speed-up the check using the VGA dirty bits. In practice
- * this is only used if your kernel driver does not implement.
- *
- * This function assumes that the framebuffers are in linear memory.
- * This may change later when we want to support larger framebuffers
- * that exceed the max DMA aperture size though.
- */
-static int
-compute_fb_update_rect_linear(FbUpdateState*  fbs,
-                              uint32_t        dirty_base,
-                              FbUpdateRect*   rect)
-{
-    int  yy;
-    int  width = fbs->width;
-    const uint8_t* src_line = fbs->src_pixels;
-    uint8_t*       dst_line = fbs->dst_pixels;
-    uint32_t       dirty_addr = dirty_base;
-    rect->xmin = rect->ymin = INT_MAX;
-    rect->xmax = rect->ymax = INT_MIN;
-    for (yy = 0; yy < fbs->height; yy++) {
-        int xx1, xx2;
-        /* If dirty_addr is != 0, then use it as a physical address to
-         * use the VGA dirty bits table to speed up the detection of
-         * changed pixels.
-         */
-        if (dirty_addr != 0) {
-            int  dirty = 0;
-            int  len   = fbs->src_pitch;
-
-            while (len > 0) {
-                int  len2 = TARGET_PAGE_SIZE - (dirty_addr & (TARGET_PAGE_SIZE-1));
-
-                if (len2 > len)
-                    len2 = len;
-
-                dirty |= cpu_physical_memory_get_dirty(dirty_addr, VGA_DIRTY_FLAG);
-                dirty_addr  += len2;
-                len         -= len2;
-            }
-
-            if (!dirty) { /* this line was not modified, skip to next one */
-                goto NEXT_LINE;
-            }
-        }
-
-        /* Then compute actual bounds of the changed pixels, while
-         * copying them from 'src' to 'dst'. This depends on the pixel depth.
-         */
-        switch (fbs->bytes_per_pixel) {
-        case 2:
-        {
-            const uint16_t* src = (const uint16_t*) src_line;
-            uint16_t*       dst = (uint16_t*) dst_line;
-
-            xx1 = 0;
-            DUFF4(width, {
-                uint16_t spix = src[xx1];
-#if defined(HOST_WORDS_BIGENDIAN) != defined(TARGET_WORDS_BIGENDIAN)
-                spix = (uint16_t)((spix << 8) | (spix >> 8));
-#endif
-                if (spix != dst[xx1])
-                    break;
-                xx1++;
-            });
-            if (xx1 == width) {
-                break;
-            }
-            xx2 = width-1;
-            DUFF4(xx2-xx1, {
-                if (src[xx2] != dst[xx2])
-                    break;
-                xx2--;
-            });
-#if defined(HOST_WORDS_BIGENDIAN) != defined(TARGET_WORDS_BIGENDIAN)
-            /* Convert the guest pixels into host ones */
-            int xx = xx1;
-            DUFF4(xx2-xx1+1,{
-                unsigned   spix = src[xx];
-                dst[xx] = (uint16_t)((spix << 8) | (spix >> 8));
-                xx++;
-            });
-#else
-            memcpy( dst+xx1, src+xx1, (xx2-xx1+1)*2 );
-#endif
-            break;
-        }
-
-        case 3:
-        {
-            xx1 = 0;
-            DUFF4(width, {
-                int xx = xx1*3;
-                if (src_line[xx+0] != dst_line[xx+0] ||
-                    src_line[xx+1] != dst_line[xx+1] ||
-                    src_line[xx+2] != dst_line[xx+2]) {
-                    break;
-                }
-                xx1 ++;
-            });
-            if (xx1 == width) {
-                break;
-            }
-            xx2 = width-1;
-            DUFF4(xx2-xx1,{
-                int xx = xx2*3;
-                if (src_line[xx+0] != dst_line[xx+0] ||
-                    src_line[xx+1] != dst_line[xx+1] ||
-                    src_line[xx+2] != dst_line[xx+2]) {
-                    break;
-                }
-                xx2--;
-            });
-            memcpy( dst_line+xx1*3, src_line+xx1*3, (xx2-xx1+1)*3 );
-            break;
-        }
-
-        case 4:
-        {
-            const uint32_t* src = (const uint32_t*) src_line;
-            uint32_t*       dst = (uint32_t*) dst_line;
-
-            xx1 = 0;
-            DUFF4(width, {
-                uint32_t spix = src[xx1];
-#if defined(HOST_WORDS_BIGENDIAN) != defined(TARGET_WORDS_BIGENDIAN)
-                spix = (spix << 16) | (spix >> 16);
-                spix = ((spix << 8) & 0xff00ff00) | ((spix >> 8) & 0x00ff00ff);
-#endif
-                if (spix != dst[xx1]) {
-                    break;
-                }
-                xx1++;
-            });
-            if (xx1 == width) {
-                break;
-            }
-            xx2 = width-1;
-            DUFF4(xx2-xx1,{
-                if (src[xx2] != dst[xx2]) {
-                    break;
-                }
-                xx2--;
-            });
-#if defined(HOST_WORDS_BIGENDIAN) != defined(TARGET_WORDS_BIGENDIAN)
-            /* Convert the guest pixels into host ones */
-            int xx = xx1;
-            DUFF4(xx2-xx1+1,{
-                uint32_t   spix = src[xx];
-                spix = (spix << 16) | (spix >> 16);
-                spix = ((spix << 8) & 0xff00ff00) | ((spix >> 8) & 0x00ff00ff);
-                dst[xx] = spix;
-                xx++;
-            })
-#else
-            memcpy( dst+xx1, src+xx1, (xx2-xx1+1)*4 );
-#endif
-            break;
-        }
-        default:
-            return 0;
-        }
-        /* Update bounds if pixels on this line were modified */
-        if (xx1 < width) {
-            if (xx1 < rect->xmin) rect->xmin = xx1;
-            if (xx2 > rect->xmax) rect->xmax = xx2;
-            if (yy < rect->ymin) rect->ymin = yy;
-            if (yy > rect->ymax) rect->ymax = yy;
-        }
-    NEXT_LINE:
-        src_line += fbs->src_pitch;
-        dst_line += fbs->dst_pitch;
-    }
-
-    if (rect->ymin > rect->ymax) { /* nothing changed */
-        return 0;
-    }
-
-    /* Always clear the dirty VGA bits */
-    cpu_physical_memory_reset_dirty(dirty_base + rect->ymin * fbs->src_pitch,
-                                    dirty_base + (rect->ymax+1)* fbs->src_pitch,
-                                    VGA_DIRTY_FLAG);
-    return 1;
-}
-
-
 static void goldfish_fb_update_display(void *opaque)
 {
     struct goldfish_fb_state *s = (struct goldfish_fb_state *)opaque;
-    uint32_t base;
-    uint8_t*  dst_line;
-    uint8_t*  src_line;
+    DisplaySurface *ds = qemu_console_surface(s->con);
     int full_update = 0;
     int  width, height, pitch;
 
-    base = s->fb_base;
-    if(base == 0)
+    if (!s || !s->con || surface_bits_per_pixel(ds) == 0 || !s->fb_base)
         return;
 
     if((s->int_enable & FB_INT_VSYNC) && !(s->int_status & FB_INT_VSYNC)) {
         s->int_status |= FB_INT_VSYNC;
-        goldfish_device_set_irq(&s->dev, 0, 1);
+        qemu_irq_raise(s->irq);
     }
 
     if(s->need_update) {
@@ -377,31 +176,17 @@
         if(s->need_int) {
             s->int_status |= FB_INT_BASE_UPDATE_DONE;
             if(s->int_enable & FB_INT_BASE_UPDATE_DONE)
-                goldfish_device_set_irq(&s->dev, 0, 1);
+                qemu_irq_raise(s->irq);
         }
         s->need_int = 0;
         s->need_update = 0;
     }
 
-    src_line  = qemu_get_ram_ptr( base );
+    pitch     = surface_stride(ds);
+    width     = surface_width(ds);
+    height    = surface_height(ds);
 
-    dst_line  = s->ds->surface->data;
-    pitch     = s->ds->surface->linesize;
-    width     = s->ds->surface->width;
-    height    = s->ds->surface->height;
-
-    FbUpdateState  fbs;
-    FbUpdateRect   rect;
-
-    fbs.width      = width;
-    fbs.height     = height;
-    fbs.dst_pixels = dst_line;
-    fbs.dst_pitch  = pitch;
-    fbs.bytes_per_pixel = goldfish_fb_get_bytes_per_pixel(s);
-
-    fbs.src_pixels = src_line;
-    fbs.src_pitch  = width*s->ds->surface->pf.bytes_per_pixel;
-
+    int ymin, ymax;
 
 #if STATS
     if (full_update)
@@ -410,8 +195,7 @@
         stats_total               += stats_counter;
         stats_total_full_updates  += stats_full_updates;
 
-        printf( "full update stats:  peak %.2f %%  total %.2f %%\n",
-                stats_full_updates*100.0/stats_counter,
+        trace_goldfish_fb_update_stats(stats_full_updates*100.0/stats_counter,
                 stats_total_full_updates*100.0/stats_total );
 
         stats_counter      = 0;
@@ -421,30 +205,53 @@
 
     if (s->blank)
     {
+        void *dst_line = surface_data(ds);
         memset( dst_line, 0, height*pitch );
-        rect.xmin = 0;
-        rect.ymin = 0;
-        rect.xmax = width-1;
-        rect.ymax = height-1;
+        ymax = height-1;
     }
     else
     {
-        if (full_update) { /* don't use dirty-bits optimization */
-            base = 0;
-        }
-        if (compute_fb_update_rect_linear(&fbs, base, &rect) == 0) {
+        SysBusDevice *dev = SYS_BUS_DEVICE(opaque);
+        MemoryRegion *address_space = sysbus_address_space(dev);
+        int src_width = width * 2;
+        int dest_col_pitch = surface_bytes_per_pixel(ds);
+        int dest_row_pitch = surface_stride(ds);
+        drawfn fn;
+
+        switch (surface_bits_per_pixel(ds)) {
+        case 0:
+            return;
+        case 8:
+            fn = draw_line_8;
+            break;
+        case 15:
+            fn = draw_line_15;
+            break;
+        case 16:
+            fn = draw_line_16;
+            break;
+        case 24:
+            fn = draw_line_24;
+            break;
+        case 32:
+            fn = draw_line_32;
+            break;
+        default:
+            hw_error("goldfish_fb: bad color depth\n");
             return;
         }
+
+        ymin = 0;
+        framebuffer_update_display(ds, address_space, s->fb_base, width, height,
+                src_width, dest_row_pitch, dest_col_pitch, full_update,
+                fn, ds, &ymin, &ymax);
     }
 
-    rect.xmax += 1;
-    rect.ymax += 1;
-#if 0
-    printf("goldfish_fb_update_display (y:%d,h:%d,x=%d,w=%d)\n",
-           rect.ymin, rect.ymax-rect.ymin, rect.xmin, rect.xmax-rect.xmin);
-#endif
-
-    dpy_update(s->ds, rect.xmin, rect.ymin, rect.xmax-rect.xmin, rect.ymax-rect.ymin);
+    ymax += 1;
+    if (ymin >= 0) {
+        trace_goldfish_fb_update_display(ymin, ymax-ymin, 0, width);
+        dpy_gfx_update(s->con, 0, ymin, width, ymax-ymin);
+    }
 }
 
 static void goldfish_fb_invalidate_display(void * opaque)
@@ -454,55 +261,58 @@
     s->need_update = 1;
 }
 
-static uint32_t goldfish_fb_read(void *opaque, target_phys_addr_t offset)
+static uint64_t goldfish_fb_read(void *opaque, hwaddr offset, unsigned size)
 {
-    uint32_t ret;
+    uint64_t ret = 0;
     struct goldfish_fb_state *s = opaque;
+    DisplaySurface *ds = qemu_console_surface(s->con);
 
     switch(offset) {
         case FB_GET_WIDTH:
-            ret = ds_get_width(s->ds);
-            //printf("FB_GET_WIDTH => %d\n", ret);
-            return ret;
+            ret = surface_width(ds);
+            break;
 
         case FB_GET_HEIGHT:
-            ret = ds_get_height(s->ds);
-            //printf( "FB_GET_HEIGHT = %d\n", ret);
-            return ret;
+            ret = surface_height(ds);
+            break;
 
         case FB_INT_STATUS:
             ret = s->int_status & s->int_enable;
             if(ret) {
                 s->int_status &= ~ret;
-                goldfish_device_set_irq(&s->dev, 0, 0);
+                qemu_irq_lower(s->irq);
             }
-            return ret;
+            break;
 
         case FB_GET_PHYS_WIDTH:
-            ret = pixels_to_mm( ds_get_width(s->ds), s->dpi );
-            //printf( "FB_GET_PHYS_WIDTH => %d\n", ret );
-            return ret;
+            ret = pixels_to_mm( surface_width(ds), s->dpi );
+            break;
 
         case FB_GET_PHYS_HEIGHT:
-            ret = pixels_to_mm( ds_get_height(s->ds), s->dpi );
-            //printf( "FB_GET_PHYS_HEIGHT => %d\n", ret );
-            return ret;
+            ret = pixels_to_mm( surface_height(ds), s->dpi );
+            break;
 
         default:
-            cpu_abort (cpu_single_env, "goldfish_fb_read: Bad offset %x\n", offset);
-            return 0;
+            error_report("goldfish_fb_read: Bad offset 0x" TARGET_FMT_plx,
+                    offset);
+            break;
     }
+
+    trace_goldfish_fb_memory_read(offset, ret);
+    return ret;
 }
 
-static void goldfish_fb_write(void *opaque, target_phys_addr_t offset,
-                        uint32_t val)
+static void goldfish_fb_write(void *opaque, hwaddr offset, uint64_t val,
+        unsigned size)
 {
     struct goldfish_fb_state *s = opaque;
 
+    trace_goldfish_fb_memory_write(offset, val);
+
     switch(offset) {
         case FB_INT_ENABLE:
             s->int_enable = val;
-            goldfish_device_set_irq(&s->dev, 0, (s->int_status & s->int_enable));
+            qemu_set_irq(s->irq, s->int_status & s->int_enable);
             break;
         case FB_SET_BASE:
             s->fb_base = val;
@@ -514,10 +324,9 @@
                 //printf("FB_SET_BASE: rotation : %d => %d\n", s->rotation, s->set_rotation);
                 s->rotation = s->set_rotation;
             }
-            goldfish_device_set_irq(&s->dev, 0, (s->int_status & s->int_enable));
+            qemu_set_irq(s->irq, s->int_status & s->int_enable);
             break;
         case FB_SET_ROTATION:
-            //printf( "FB_SET_ROTATION %d\n", val);
             s->set_rotation = val;
             break;
         case FB_SET_BLANK:
@@ -525,42 +334,64 @@
             s->need_update = 1;
             break;
         default:
-            cpu_abort (cpu_single_env, "goldfish_fb_write: Bad offset %x\n", offset);
+            error_report("goldfish_fb_write: Bad offset 0x" TARGET_FMT_plx,
+                    offset);
     }
 }
 
-static CPUReadMemoryFunc *goldfish_fb_readfn[] = {
-   goldfish_fb_read,
-   goldfish_fb_read,
-   goldfish_fb_read
+static const MemoryRegionOps goldfish_fb_iomem_ops = {
+    .read = goldfish_fb_read,
+    .write = goldfish_fb_write,
+    .endianness = DEVICE_NATIVE_ENDIAN,
+    .impl.min_access_size = 4,
+    .impl.max_access_size = 4,
 };
 
-static CPUWriteMemoryFunc *goldfish_fb_writefn[] = {
-   goldfish_fb_write,
-   goldfish_fb_write,
-   goldfish_fb_write
+static const GraphicHwOps goldfish_fb_ops = {
+    .invalidate = goldfish_fb_invalidate_display,
+    .gfx_update = goldfish_fb_update_display,
 };
 
-void goldfish_fb_init(int id)
+static int goldfish_fb_init(SysBusDevice *sbdev)
 {
-    struct goldfish_fb_state *s;
+    DeviceState *dev = DEVICE(sbdev);
+    struct goldfish_fb_state *s = GOLDFISH_FB(dev);
 
-    s = (struct goldfish_fb_state *)qemu_mallocz(sizeof(*s));
-    s->dev.name = "goldfish_fb";
-    s->dev.id = id;
-    s->dev.size = 0x1000;
-    s->dev.irq_count = 1;
+    sysbus_init_irq(sbdev, &s->irq);
 
-    s->ds = graphic_console_init(goldfish_fb_update_display,
-                                 goldfish_fb_invalidate_display,
-                                 NULL,
-                                 NULL,
-                                 s);
+    s->con = graphic_console_init(dev, 0, &goldfish_fb_ops, s);
 
     s->dpi = 165;  /* XXX: Find better way to get actual value ! */
 
-    goldfish_device_add(&s->dev, goldfish_fb_readfn, goldfish_fb_writefn, s);
+    memory_region_init_io(&s->iomem, OBJECT(s), &goldfish_fb_iomem_ops, s,
+            "goldfish_fb", 0x100);
+    sysbus_init_mmio(sbdev, &s->iomem);
 
-    register_savevm( "goldfish_fb", 0, GOLDFISH_FB_SAVE_VERSION,
+    register_savevm(dev, "goldfish_fb", 0, GOLDFISH_FB_SAVE_VERSION,
                      goldfish_fb_save, goldfish_fb_load, s);
+
+    return 0;
 }
+
+static void goldfish_fb_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
+
+    k->init = goldfish_fb_init;
+    dc->desc = "goldfish framebuffer";
+}
+
+static const TypeInfo goldfish_fb_info = {
+    .name          = TYPE_GOLDFISH_FB,
+    .parent        = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof(struct goldfish_fb_state),
+    .class_init    = goldfish_fb_class_init,
+};
+
+static void goldfish_fb_register(void)
+{
+    type_register_static(&goldfish_fb_info);
+}
+
+type_init(goldfish_fb_register);
diff --git a/trace-events b/trace-events
index b6d289d..7d71d23 100644
--- a/trace-events
+++ b/trace-events
@@ -1252,3 +1252,9 @@
 # hw/pci/pci_host.c
 pci_cfg_read(const char *dev, unsigned devid, unsigned fnid, unsigned offs, unsigned val) "%s %02u:%u @0x%x -> 0x%x"
 pci_cfg_write(const char *dev, unsigned devid, unsigned fnid, unsigned offs, unsigned val) "%s %02u:%u @0x%x <- 0x%x"
+
+# hw/display/goldfish_fb.c
+goldfish_fb_memory_read(uint32_t addr, uint32_t value) "addr %08x value %08x"
+goldfish_fb_memory_write(uint32_t addr, uint32_t value) "addr %08x value %08x"
+goldfish_fb_update_display(int y, int h, int x, int w) "y:%d,h:%d,x=%d,w=%d"
+goldfish_fb_update_stats(float peak, float total) "peak %.2f %%  total %.2f %%"
\ No newline at end of file