diff --git a/Bindings/Utilities.hs b/Bindings/Utilities.hs
--- a/Bindings/Utilities.hs
+++ b/Bindings/Utilities.hs
@@ -1,6 +1,6 @@
-module Bindings.Utilities (
-	storableCast,
-	storableCastArray,
+module Bindings.Utilities
+  ( storableCast
+  , storableCastArray
   ) where
 
 import Foreign.C
diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,152 @@
+# Changelog
+
+This repository releases eleven packages, each versioned independently under the
+PVP and tagged as `<pkg>-<version>` (see [RELEASE.md](RELEASE.md)). Each entry
+below names the package and version it shipped in.
+
+## bindings-DSL 1.1.0
+
+- Add `#array2d_field` for members like `char x[8][255]`, which `#array_field`
+  miscompiled: it divided the member size by `sizeOf (Ptr CChar)` and produced
+  255 rows instead of 8. Declare the type as the row pointer (`Ptr CChar`);
+  `peek` returns pointers into the struct, `poke` memmoves each row back.
+
+- `#array_field` and `#union_array_field` element counts are now baked in from C
+  (`sizeof member / sizeof member[0]`) instead of dividing by the declared
+  Haskell type's `sizeOf` at runtime. Beware: if your declared type's `sizeOf`
+  does not match the C element size, the count changes. The old code never read
+  past the member; the new code reads `count * sizeOf` bytes and will overrun it
+  if the declared type is too big. Correctly declared bindings generate identical
+  code.
+
+- The code generated by `#stoptype` now carries `{-# LINE #-}` pragmas pointing
+  back into the `.hsc` file, so GHC reports an error in a struct field at that
+  field rather than at an unrelated line before the `#starttype` block.
+
+## bindings-gpgme 0.2.0
+
+- Hide the trust item API when building against gpgme 2.0, which removed it
+  (deprecated since 1.14), so the binding compiles against both the 1.x and 2.x
+  series.
+
+- Add the ECC, ECDSA, ECDH and EDDSA public key algorithm constants, and
+  `gpgme_op_delete_ext`.
+
+- Catch up with the API added between gpgme 1.6 and 2.1, which the binding had
+  never covered. Key creation and editing (`createkey`, `adduid`, `keysign`,
+  `setexpire`, `interact` and friends), context flags (offline, sender, status
+  callback, `set_ctx_flag`), the encrypt, decrypt, export and keylist mode flags,
+  importing and exporting by key rather than by pattern, TOFU, and the various
+  new struct members. Each is guarded on the gpgme version that introduced it, so
+  the binding still builds against older releases.
+
+- Expose the key capability and status bits (revoked, expired, can_encrypt,
+  secret, ...) as `c'gpgme_key_*`, `c'gpgme_subkey_*` and related accessors. They
+  are C bitfields, which have no `offsetof` and so could not be bound as struct
+  fields; they now go through `inlines.c`. Previously there was no way to tell
+  whether a listed key was revoked or able to encrypt.
+
+- Bind `gpgme_error_from_syserror` through `inlines.c`. It is a static inline in
+  `gpgme.h` rather than a symbol in libgpgme, so a `#ccall` linked but failed to
+  resolve at load time.
+
+- Add `gpgme_invalid_key_next`. The `next` member of `_gpgme_invalid_key` is
+  bound as an embedded struct instead of a pointer, so `invalid_recipients` and
+  `invalid_signers` cannot be walked. Fixing the field would change the type of a
+  generated accessor and break its callers, so it is left as it is and this is
+  added alongside.
+
+## bindings-hdf5 0.1.3
+
+- Hide `H5D_MPIO_FILTERS` on HDF5 1.10 and later, which removed it.
+
+## bindings-libcddb 0.3.1
+
+- Declare `CDDB_CATEGORY` as `const char*`, matching libcddb. The accessor was
+  instantiated with `char*`, so the generated return type dropped the const on
+  the pointed-to char. GCC warned about that before 14 and rejects it from 14 on.
+
+## All packages
+
+- Every `bindings-*` package accepts `bindings-DSL < 1.2`, and is released so
+  that the new bound reaches Hackage. Releases are now tagged per package
+  (`bindings-DSL-1.1.0`, `bindings-hdf5-0.1.3`, ...) rather than with a bare
+  version, which was ambiguous once more than one package was being released.
+
+- Replace Travis with GitHub Actions, building `bindings-DSL` and
+  `bindings-posix` across GHC 9.6 to 9.12, `bindings-gpgme` against the latest
+  gpgme 1.x and 2.x, `bindings-libcddb` under GCC 14, and `bindings-hdf5`.
+
+## bindings-DSL 1.0.25
+
+- No changes to the DSL itself. `bindings-posix` stopped assuming that OS X
+  provides everything POSIX asks for.
+
+## bindings-DSL 1.0.24
+
+- Add `#num_pattern` and `#fractional_pattern`, for matching numeric constants in
+  patterns.
+
+- Support interruptible calls with `#ccall_interruptible`.
+
+- Silence GHC warnings for `Storable` instances of field-less types, printf
+  format warnings, and shadowed temporary variables.
+
+## bindings-DSL 1.0.23
+
+- No changes to the DSL itself. `bindings-posix` gained bindings for message
+  queues.
+
+## bindings-DSL 1.0.22
+
+- No changes to the DSL itself. `bindings-gpgme` gained pinentry modes, I/O
+  wrappers and the subkey curve field; `bindings-hdf5` gained its low level
+  interface.
+
+## bindings-DSL 1.0.21
+
+- No changes to the DSL itself. Maintainership passed to John Wiegley.
+
+## bindings-DSL 1.0.20
+
+- Include `<cinttypes>` instead of `<inttypes.h>` under a C++ compiler, which
+  otherwise lacks `PRIuMAX` and `PRIdMAX` and fails to expand the macros. Note
+  that this requires building the user library with `-std=c++11`.
+
+## bindings-DSL 1.0.19
+
+- Add a change log.
+
+- Support unsafe calls with experimental `#ccall_unsafe`.
+
+## bindings-DSL 1.0.18
+
+This version has a bug. Marked as such in Hackage.
+
+- Introduce a silly bug to solve a problem that does not exist.
+
+## bindings-DSL 1.0.17
+
+- Add `Bindings.Utilities` module for general utilities.
+
+- Change repository to git.
+
+- Don't underestimate size of field arrays with dimension >= 2.
+
+- Change `#callback` to `#callback_t`.
+
+- Keep source of `bindings-*` libraries with `bindings-DSL`.
+
+- Add examples used in tutorial.
+
+## bindings-DSL 1.0.16
+
+- Use correct value for `Storable` alignment instead of copying `sizeOf`.
+
+## bindings-DSL 1.0.15
+
+- New hsc2hs template doesn't include some headers, so include it ourselves.
+
+## bindings-DSL 1.0.14
+
+- Add functions to get pointers to fields from pointers to structures.
diff --git a/ChangeLog b/ChangeLog
deleted file mode 100644
--- a/ChangeLog
+++ /dev/null
@@ -1,35 +0,0 @@
-Changes in 1.0.19
-
-* Add a change log.
-
-* Support unsafe calls with experimental #ccall_unsafe.
-
-Changes in 1.0.18 (This version has a bug. Marked as such in Hackage)
-
-* Introduce a silly bug to solve a problem that does not exist.
-
-Changes in 1.0.17
-
-* Add Bindings.Utilities module for general utilities.
-
-* Change repository to git.
-
-* Don't underestimate size of field arrays with dimension >= 2.
-
-* Change #callback to #callback_t.
-
-* Keep source of bindings-* libraries with bindings-DSL.
-
-* Add examples used in tutorial.
-
-Changes in 1.0.16
-
-* Use correct value for Storable alignment instead of copying sizeOf.
-
-Changes in 1.0.15
-
-* New hsc2hs template doesn't include some headers, so include it ourselves.
-
-Changes in 1.0.14
-
-* Add functions to get pointers to fields from pointers to structures.
diff --git a/README.md b/README.md
new file mode 100644
--- /dev/null
+++ b/README.md
@@ -0,0 +1,134 @@
+# bindings-dsl
+
+[![Hackage](https://img.shields.io/hackage/v/bindings-DSL.svg)](https://hackage.haskell.org/package/bindings-DSL)
+[![CI](https://github.com/rethab/bindings-dsl/actions/workflows/ci.yml/badge.svg)](https://github.com/rethab/bindings-dsl/actions/workflows/ci.yml)
+![BSD3 License](https://img.shields.io/github/license/rethab/bindings-dsl?label=license)
+
+`bindings-DSL` is a domain specific language for writing Haskell FFI bindings, on
+top of [hsc2hs](https://downloads.haskell.org/ghc/latest/docs/users_guide/utils.html#writing-haskell-interfaces-to-c-code-hsc2hs).
+It is a set of C macros that describe a C interface, and from which hsc2hs
+extracts the Haskell code that mimics it: `#ccall` for a function, `#starttype`
+and `#field` for a struct, `#num` for a constant, and so on.
+
+```haskell
+#include <bindings.dsl.h>
+#include <gpgme.h>
+
+module Bindings.Gpgme where
+#strict_import
+
+#num GPGME_PK_RSA
+
+#starttype struct _gpgme_key
+#field revoked , CUInt
+#field fpr     , CString
+#stoptype
+
+#ccall gpgme_get_key , <gpgme_ctx_t> -> CString -> Ptr <gpgme_key_t> -> CInt -> IO <gpgme_error_t>
+```
+
+Haskell names are derived from the C names automatically, so there is no naming
+scheme to invent and no chance of the two drifting apart: a constant becomes
+`c'GPGME_PK_RSA`, a struct becomes a `Storable` instance with `p'_gpgme_key'fpr`
+accessors, and a function becomes `c'gpgme_get_key`. Structs are laid out by the
+C compiler rather than by hand, which is what makes the bindings portable across
+platforms where sizes and padding differ. There are also macros for binding
+inline functions and macro functions, which have no symbol to call.
+
+Full documentation, including a tutorial, is on the
+[wiki](https://github.com/rethab/bindings-dsl/wiki). Release notes are in
+[CHANGELOG.md](CHANGELOG.md).
+
+## Packages
+
+`bindings-DSL` is the DSL itself. The bindings built on top of it are released as
+separate packages from this repository:
+
+| Package | Hackage |
+| --- | --- |
+| [bindings-directfb](bindings-directfb) | [![Hackage](https://img.shields.io/hackage/v/bindings-directfb.svg?label=)](https://hackage.haskell.org/package/bindings-directfb) |
+| [bindings-fann](bindings-fann) | [![Hackage](https://img.shields.io/hackage/v/bindings-fann.svg?label=)](https://hackage.haskell.org/package/bindings-fann) |
+| [bindings-glib](bindings-glib) | [![Hackage](https://img.shields.io/hackage/v/bindings-glib.svg?label=)](https://hackage.haskell.org/package/bindings-glib) |
+| [bindings-gpgme](bindings-gpgme) | [![Hackage](https://img.shields.io/hackage/v/bindings-gpgme.svg?label=)](https://hackage.haskell.org/package/bindings-gpgme) |
+| [bindings-gsl](bindings-gsl) | [![Hackage](https://img.shields.io/hackage/v/bindings-gsl.svg?label=)](https://hackage.haskell.org/package/bindings-gsl) |
+| [bindings-hdf5](bindings-hdf5) | [![Hackage](https://img.shields.io/hackage/v/bindings-hdf5.svg?label=)](https://hackage.haskell.org/package/bindings-hdf5) |
+| [bindings-libcddb](bindings-libcddb) | [![Hackage](https://img.shields.io/hackage/v/bindings-libcddb.svg?label=)](https://hackage.haskell.org/package/bindings-libcddb) |
+| [bindings-libffi](bindings-libffi) | [![Hackage](https://img.shields.io/hackage/v/bindings-libffi.svg?label=)](https://hackage.haskell.org/package/bindings-libffi) |
+| [bindings-posix](bindings-posix) | [![Hackage](https://img.shields.io/hackage/v/bindings-posix.svg?label=)](https://hackage.haskell.org/package/bindings-posix) |
+| [bindings-sqlite3](bindings-sqlite3) | [![Hackage](https://img.shields.io/hackage/v/bindings-sqlite3.svg?label=)](https://hackage.haskell.org/package/bindings-sqlite3) |
+
+## Contributing
+
+Contributions are welcome, whether to the DSL itself or to any of the bindings.
+Bugs, missing coverage of a C API, and build problems can be raised in the
+[issue tracker](https://github.com/rethab/bindings-dsl/issues); pull requests are
+just as welcome. CI builds the DSL and the bindings across several GHC versions
+and library releases, so open a pull request and let it run.
+
+## Building bindings-gpgme on macOS with Homebrew
+
+`gpgme.h` includes `<gpg-error.h>`, and Homebrew ships `libgpg-error` as a keg of
+its own, so both prefixes have to be on the include and library paths. Passing
+only gpgme's prefix leaves the nested include unresolved and the build fails with
+a misleading complaint about a missing `gpgme.h`.
+
+```sh
+brew install gpgme libgpg-error
+
+cabal build bindings-gpgme \
+  --extra-include-dirs="$(brew --prefix gpgme)/include" \
+  --extra-include-dirs="$(brew --prefix libgpg-error)/include" \
+  --extra-lib-dirs="$(brew --prefix gpgme)/lib" \
+  --extra-lib-dirs="$(brew --prefix libgpg-error)/lib"
+```
+
+With stack, list both prefixes in `stack.yaml` (`brew --prefix` is
+`/opt/homebrew` on Apple silicon and `/usr/local` on Intel):
+
+```yaml
+extra-include-dirs:
+  - /opt/homebrew/opt/gpgme/include
+  - /opt/homebrew/opt/libgpg-error/include
+extra-lib-dirs:
+  - /opt/homebrew/opt/gpgme/lib
+  - /opt/homebrew/opt/libgpg-error/lib
+```
+
+## Cross compilation
+
+bindings-DSL does not work with `hsc2hs --cross-compile` (see
+[#38](https://github.com/rethab/bindings-dsl/issues/38)). Every construct
+(`#strict_import`, `#starttype`, `#ccall`, ...) is a custom hsc2hs construct that
+emits Haskell while the generated C program runs. Cross mode never runs that
+program: it recognises only a fixed set of built-in directives and rejects
+everything else with
+
+```
+directive strict_import cannot be handled in cross-compilation mode
+```
+
+`--via-asm` does not help, it only changes how cross mode extracts constants. No
+change to `bindings.dsl.h` can lift this; it needs hsc2hs to support custom
+constructs when cross compiling.
+
+Cross compiling still works if you can execute target binaries, e.g. with
+qemu-user plus `binfmt_misc`, or wine for Windows targets. Cabal gets in the way
+here: it passes `-x` to hsc2hs whenever the host platform differs from the build
+platform, and hsc2hs has no flag to undo that. Point Cabal at a wrapper that
+drops the flag and lets the normal compile-and-run path proceed under emulation:
+
+```sh
+#!/bin/sh
+# hsc2hs-no-cross, used via --with-hsc2hs=/path/to/hsc2hs-no-cross
+for a in "$@"; do
+  shift
+  case "$a" in
+    -x|--cross-compile) ;;
+    *) set -- "$@" "$a" ;;
+  esac
+done
+exec hsc2hs "$@"
+```
+
+Otherwise, write plain `.hsc` without bindings-DSL for the modules you need to
+cross compile.
diff --git a/bindings-DSL.cabal b/bindings-DSL.cabal
--- a/bindings-DSL.cabal
+++ b/bindings-DSL.cabal
@@ -1,6 +1,6 @@
-cabal-version: >= 1.8
+cabal-version: >= 1.10
 name: bindings-DSL
-homepage: https://github.com/jwiegley/bindings-dsl/wiki
+homepage: https://github.com/rethab/bindings-dsl/wiki
 synopsis:
   FFI domain specific language, on top of hsc2hs.
 description: 
@@ -13,29 +13,32 @@
   to help write bindings to inline functions or macro functions.
   Documentation is available at package homepage:
   .
-  <https://github.com/jwiegley/bindings-dsl/wiki>
+  <https://github.com/rethab/bindings-dsl/wiki>
   .
   The extra module Bindings.Utilities will contain tools that may
   be convenient when working with FFI.
-version: 1.0.25
+version: 1.1.0
 license: BSD3
 license-file: LICENSE
-maintainer: John Wiegley <johnw@newartisans.com>
+maintainer: Reto <rethab@protonmail.ch>
 author: Maurício C. Antunes
 stability: Stable API, well tested, portable, used in commercial code.
 build-type: Simple
-bug-reports: https://github.com/jwiegley/bindings-dsl/issues
+bug-reports: https://github.com/rethab/bindings-dsl/issues
 category: FFI
-extra-source-files: ChangeLog
+extra-source-files: README.md , CHANGELOG.md
 library
+  default-language:
+    Haskell2010
+  include-dirs: .
   install-includes: bindings.dsl.h , bindings.cmacros.h
-  build-depends: base >= 0 && < 1000
+  build-depends: base >= 4 && < 5
   exposed-modules: Bindings.Utilities
 source-repository head
   type: git
-  location: git://github.com/jwiegley/bindings-dsl
+  location: https://github.com/rethab/bindings-dsl
   branch: master
 source-repository this
   type: git
-  location: git://github.com/jwiegley/bindings-dsl
-  tag: 1.0.25
+  location: https://github.com/rethab/bindings-dsl
+  tag: bindings-DSL-1.1.0
diff --git a/bindings.dsl.h b/bindings.dsl.h
--- a/bindings.dsl.h
+++ b/bindings.dsl.h
@@ -18,6 +18,25 @@
 #include <inttypes.h>
 #endif
 
+static char bc_srcfile[1000] = "";
+
+/* hsc2hs calls hsc_line before every chunk of Haskell output. We keep the
+ * original behaviour and additionally remember the file name, so that the
+ * code generated by hsc_stoptype can be attributed to the .hsc source. */
+#undef hsc_line
+#define hsc_line(line,file) \
+    { \
+     strncpy(bc_srcfile,file,sizeof bc_srcfile - 1); \
+     bc_srcfile[sizeof bc_srcfile - 1] = '\0'; \
+     printf("{-# LINE %d \"%s\" #-}\n",line,file); \
+    } \
+
+#define bc_lineprag(l) \
+    { \
+     if (bc_srcfile[0]) \
+        printf("{-# LINE %d \"%s\" #-}\n",(int)(l),bc_srcfile); \
+    } \
+
 #define hsc_strict_import(dummy) printf( \
     "import Foreign.Ptr (Ptr,FunPtr,plusPtr)\n" \
     "import Foreign.Ptr (wordPtrToPtr,castPtrToFunPtr)\n" \
@@ -26,6 +45,7 @@
     "import Foreign.C.String (CString,CStringLen,CWString,CWStringLen)\n" \
     "import Foreign.Marshal.Alloc (alloca)\n" \
     "import Foreign.Marshal.Array (peekArray,pokeArray)\n" \
+    "import Foreign.Marshal.Utils (moveBytes)\n" \
     "import Data.Int\n" \
     "import Data.Word\n" \
     ); \
@@ -222,8 +242,8 @@
     printf(" -> (");bc_typemarkup(# type);printf(")\n"); \
 
 static struct {
-	int n, is_union[500], is_fam[500];
-	uintmax_t array_size[500], offset[500];
+	int n, is_union[500], is_fam[500], is_2d[500], line[500];
+	uintmax_t array_size[500], elem_size[500], offset[500];
 	char fname[500][1000], ftype[500][1000];
 } bc_fielddata;
 
@@ -240,6 +260,7 @@
      ptrdiff_t typealign = (char*)&bc_refdata.v - (char*)&bc_refdata; \
      bc_fielddata.n = 0; \
      char typename[] = # name; \
+     int typeline = __LINE__; \
      int index; \
      int standalone_deriving = 0; \
 
@@ -248,8 +269,11 @@
      bc_fielddata.offset[index] = (uintmax_t) \
          ((char*)&bc_refdata.v.name - (char*)&bc_refdata.v); \
      bc_fielddata.array_size[index] = 0; \
+     bc_fielddata.elem_size[index] = 0; \
      bc_fielddata.is_union[index] = u; \
      bc_fielddata.is_fam[index] = f; \
+     bc_fielddata.is_2d[index] = 0; \
+     bc_fielddata.line[index] = __LINE__; \
      strcpy(bc_fielddata.fname[index],# name); \
      strcpy(bc_fielddata.ftype[index],type); \
 
@@ -264,13 +288,22 @@
 
 #define hsc_array_field(name,type) \
      bc_basicfield(name,# type,0,0); \
-     bc_fielddata.array_size[index] = sizeof bc_refdata.v.name \
+     bc_fielddata.array_size[index] = sizeof bc_refdata.v.name; \
+     bc_fielddata.elem_size[index] = sizeof bc_refdata.v.name[0] \
 
 #define hsc_union_array_field(name,type) \
      bc_basicfield(name,# type,1,0); \
-     bc_fielddata.array_size[index] = sizeof bc_refdata.v.name \
+     bc_fielddata.array_size[index] = sizeof bc_refdata.v.name; \
+     bc_fielddata.elem_size[index] = sizeof bc_refdata.v.name[0] \
 
+#define hsc_array2d_field(name,type) \
+     bc_basicfield(name,# type,0,0); \
+     bc_fielddata.array_size[index] = sizeof bc_refdata.v.name; \
+     bc_fielddata.elem_size[index] = sizeof bc_refdata.v.name[0]; \
+     bc_fielddata.is_2d[index] = 1 \
+
 #define hsc_stoptype(dummy) \
+     bc_lineprag(typeline); \
      printf("data ");bc_conid(typename);printf(" = "); \
      bc_conid(typename);printf("{\n"); \
      int i; \
@@ -297,14 +330,17 @@
         } \
      for (i=0; i < bc_fielddata.n; i++) \
         { \
+         bc_lineprag(bc_fielddata.line[i]); \
          bc_fieldoffset(typename,bc_fielddata.fname[i]); \
          printf(" p = plusPtr p %" PRIuMAX "\n",bc_fielddata.offset[i]); \
+         bc_lineprag(bc_fielddata.line[i]); \
          bc_fieldoffset(typename,bc_fielddata.fname[i]); \
          printf(" :: Ptr (");bc_conid(typename);printf(") -> "); \
          printf("Ptr (");bc_typemarkup(bc_fielddata.ftype[i]);printf(")\n"); \
         } \
      for (i=0; i < bc_fielddata.n; i++) if (bc_fielddata.is_union[i]) \
         { \
+         bc_lineprag(bc_fielddata.line[i]); \
          bc_unionupdate(typename,bc_fielddata.fname[i]); \
          printf(" :: ");bc_conid(typename);printf(" -> "); \
          if (bc_fielddata.array_size[i] > 0) printf("["); \
@@ -316,13 +352,9 @@
          printf(" v vf = alloca $ \\p -> do\n"); \
          printf("  poke p v\n"); \
          if (bc_fielddata.array_size[i] > 0) \
-            { \
-             printf("  let s%d = div %" PRIuMAX " $ sizeOf $ (undefined :: ", \
-               i, bc_fielddata.array_size[i]); \
-             bc_typemarkup(bc_fielddata.ftype[i]); \
-             printf(")\n  pokeArray (plusPtr p %" PRIuMAX ") $ take s%d vf", \
-               bc_fielddata.offset[i], i); \
-            } \
+             printf("  pokeArray (plusPtr p %" PRIuMAX ") $ take %" PRIuMAX " vf", \
+               bc_fielddata.offset[i], \
+               bc_fielddata.array_size[i] / bc_fielddata.elem_size[i]); \
          else \
            printf("  pokeByteOff p %" PRIuMAX " vf", \
                bc_fielddata.offset[i]); \
@@ -337,6 +369,7 @@
              printf(" vu}\n"); \
             } \
         } \
+     bc_lineprag(typeline); \
      printf("instance Storable "); \
      bc_conid(typename);printf(" where\n"); \
      printf("  sizeOf _ = %" PRIuMAX "\n  alignment _ = %" PRIuMAX "\n", \
@@ -347,14 +380,15 @@
          printf("    v%d <- ",i); \
          if (bc_fielddata.is_fam[i]) \
             printf("return []"); \
+         else if (bc_fielddata.is_2d[i]) \
+            printf("return [plusPtr _p (%" PRIuMAX " + %" PRIuMAX " * k) " \
+                   "| k <- [0 .. %" PRIuMAX "]]", \
+              bc_fielddata.offset[i], bc_fielddata.elem_size[i], \
+              bc_fielddata.array_size[i] / bc_fielddata.elem_size[i] - 1); \
          else if (bc_fielddata.array_size[i] > 0) \
-           { \
-            printf ("let s%d = div %" PRIuMAX " $ sizeOf $ (undefined :: ", \
-              i, bc_fielddata.array_size[i]); \
-            bc_typemarkup(bc_fielddata.ftype[i]); \
-            printf(") in peekArray s%d (plusPtr _p %" PRIuMAX ")", \
-              i, bc_fielddata.offset[i]); \
-           } \
+            printf("peekArray %" PRIuMAX " (plusPtr _p %" PRIuMAX ")", \
+              bc_fielddata.array_size[i] / bc_fielddata.elem_size[i], \
+              bc_fielddata.offset[i]); \
          else \
             printf("peekByteOff _p %" PRIuMAX "", bc_fielddata.offset[i]); \
          printf("\n"); \
@@ -370,14 +404,18 @@
          if (bc_fielddata.is_fam[i]) \
             printf("    pokeArray (plusPtr _p %" PRIuMAX ") v%d", \
               bc_fielddata.offset[i],i); \
+         else if (bc_fielddata.is_2d[i]) \
+            printf("    sequence_ [moveBytes (plusPtr _p (%" PRIuMAX \
+                   " + %" PRIuMAX " * k)) r %" PRIuMAX \
+                   " | (k,r) <- zip [0 .. %" PRIuMAX "] v%d]", \
+              bc_fielddata.offset[i], bc_fielddata.elem_size[i], \
+              bc_fielddata.elem_size[i], \
+              bc_fielddata.array_size[i] / bc_fielddata.elem_size[i] - 1, i); \
          else if (bc_fielddata.array_size[i] > 0) \
-           { \
-            printf("    let s%d = div %" PRIuMAX " $ sizeOf $ (undefined :: ", \
-              i, bc_fielddata.array_size[i]); \
-            bc_typemarkup(bc_fielddata.ftype[i]); \
-            printf(")\n    pokeArray (plusPtr _p %" PRIuMAX ") (take s%d v%d)", \
-              bc_fielddata.offset[i], i, i); \
-           } \
+            printf("    pokeArray (plusPtr _p %" PRIuMAX ") (take %" PRIuMAX \
+                   " v%d)", \
+              bc_fielddata.offset[i], \
+              bc_fielddata.array_size[i] / bc_fielddata.elem_size[i], i); \
          else \
             printf("    pokeByteOff _p %" PRIuMAX " v%d", \
               bc_fielddata.offset[i],i); \
