Open a door, break a wall, reveal the next stretch of scenery. The map changes first; this routine makes the picture catch up. Give it a rectangle of byte-map IDs and a destination, and it draws just that part of the world.

Load a room while the LCD is off
This SM83 routine copies logical IDs into the Game Boy’s background map. The caller uploads four original 2bpp patterns separately at $8000–$803F; map IDs 0–3 select those same hardware pattern numbers. A scanline in a pattern is two bytes: low colour plane, then high colour plane.
The selected background map is $9800–$9BFF, 32×32 cells. At zero scroll the screen shows only 20×18 of them. A valid write to the bottom-right map cell is therefore outside the current view. Map coordinates, camera position and visible pixels are different things.
This variant has a firm rule: LCD-off calls only. It checks LCDC bit 7 and rejects the request if the LCD is running. The reset caller waits until LY is at least 145 before disabling a running LCD, following Nintendo’s early-DMG caution, then uploads assets and draws. It enables the LCD afterwards with zero scroll, unsigned pattern addressing, and window/sprites off.
Make it part of your project
Reserve a 32-byte writable NR context in WRAM, define MAP, MAP_W, MAP_H, TILE_COUNT, BG_MAP and the 32×32 screen dimensions, then include sm83.inc once. All allocations must fit their accessible bank. The twelve-by-eight source map uses one byte per logical ID; ID zero is an opaque blank pattern. No scratch initialisation call is needed.
AF/BC/DE/HL and flags are clobbered. Four stack bytes cover the direct CALL and paint helper, beyond caller and interrupt work. Keep the request, map, assets and LCD policy stable throughout the call; an interrupt must not enable the LCD halfway through copying.
Each accepted cell writes one map byte: fifteen for a 5×3 patch, plus the preflight. The four separately loaded pictures cost 64 bytes. This suits room loading or a blanked transition. A live scrolling game needs a bounded VRAM update queue or another access-safe transfer policy. CGB banked attributes and logical-to-physical pattern translation are separate variants, not capabilities hidden in this one.
Draw the patch
These are the example caller’s allocations and constants. Use addresses which fit your own memory plan.
The caller owns its memory and display configuration
DEF NR EQU 49152
DEF RESULT EQU 49216
DEF MAP EQU 49392
DEF MAP_W EQU 12
DEF MAP_H EQU 8
DEF TILE_COUNT EQU 4
DEF SCREEN_COLS EQU 32
DEF SCREEN_ROWS EQU 32
DEF BG_MAP EQU 38912Supply six unsigned bytes: source column, source row, destination column, destination row, width and height. Then call region_draw. NR+6 is one for a completed draw or zero for rejection. The six request bytes survive.
Draw a 5×3 patch from map cell (2,1) at display cell (4,3)
case_0:
ld a,2
ld [NR+0],a
ld a,1
ld [NR+1],a
ld a,4
ld [NR+2],a
ld a,3
ld [NR+3],a
ld a,5
ld [NR+4],a
ld a,3
ld [NR+5],a
call region_draw
ld a,[NR+6]
or 128
ld [RESULT+1],aThe result store after the call belongs to the diagnostic. In a game, inspect the completion flag and continue with your own logic. The routine rejects zero sizes, invalid starts, source/destination overruns and IDs outside the configured tile set. It checks every selected ID before writing any destination bytes, so a bad ID cannot leave half a patch behind. It rejects rather than clips.
Keep the reusable code
Checked native region drawing
; LCD-off background map transfer. Caller supplies byte-map and tile assets.
; Inputs NR+0..5: source X/Y, destination X/Y, width/height; NR+6=0/1.
; AF/BC/DE/HL and NR+6..31 clobbered. No interrupt or LCD ownership changes.
region_draw:
xor a
ld [NR+6],a
ld a,[$ff40]
bit 7,a
ret nz
ld a,[NR+4]
and a
ret z
ld a,[NR+0]
cp MAP_W
ret nc
ld b,a
ld a,MAP_W
sub b
ld b,a
ld a,[NR+4]
cp b
jr c,bounds_fit_0
ret nz
bounds_fit_0:
ld a,[NR+5]
and a
ret z
ld a,[NR+1]
cp MAP_H
ret nc
ld b,a
ld a,MAP_H
sub b
ld b,a
ld a,[NR+5]
cp b
jr c,bounds_fit_1
ret nz
bounds_fit_1:
ld a,[NR+4]
and a
ret z
ld a,[NR+2]
cp SCREEN_COLS
ret nc
ld b,a
ld a,SCREEN_COLS
sub b
ld b,a
ld a,[NR+4]
cp b
jr c,bounds_fit_2
ret nz
bounds_fit_2:
ld a,[NR+5]
and a
ret z
ld a,[NR+3]
cp SCREEN_ROWS
ret nc
ld b,a
ld a,SCREEN_ROWS
sub b
ld b,a
ld a,[NR+5]
cp b
jr c,bounds_fit_3
ret nz
bounds_fit_3:
xor a
ld [NR+29],a
region_pass:
xor a
ld [NR+16],a
region_row:
xor a
ld [NR+17],a
region_cell:
ld hl,MAP
ld a,[NR+1]
ld b,a
ld a,[NR+16]
add a,b
and a
jr z,source_column
ld b,a
ld de,MAP_W
source_row:
add hl,de
dec b
jr nz,source_row
source_column:
ld a,[NR]
ld b,a
ld a,[NR+17]
add a,b
ld e,a
ld d,0
add hl,de
ld a,[hl]
ld [NR+19],a
cp TILE_COUNT
ret nc
ld a,[NR+29]
and a
call nz,region_paint
ld hl,NR+17
inc [hl]
ld a,[NR+4]
cp [hl]
jp nz,region_cell
ld hl,NR+16
inc [hl]
ld a,[NR+5]
cp [hl]
jp nz,region_row
ld a,[NR+29]
and a
jr nz,region_success
inc a
ld [NR+29],a
jp region_pass
region_success:
ld a,1
ld [NR+6],a
ret
region_paint:
ld a,[NR+3]
ld b,a
ld a,[NR+16]
add a,b
ld l,a
ld h,0
add hl,hl
add hl,hl
add hl,hl
add hl,hl
add hl,hl
ld de,BG_MAP
add hl,de
ld a,[NR+2]
ld b,a
ld a,[NR+17]
add a,b
ld e,a
ld d,0
add hl,de
ld a,[NR+19]
ld [hl],a
retThe context is non-re-entrant. Your caller owns its memory, video setup and interrupt policy. Keep all inputs and display resources stable throughout the call. Drawing an opaque zero tile erases a cell; transparent sprites need masking or background restoration instead.
What was checked
All 19 calls completed on this machine in the original reset diagnostic. Fifteen rejected map/bounds/ID requests were also executed in isolation, each after the same three accepted draws. The complete destination and both mapped guards were compared with an independent model, and every native capture pixel was compared with a separate composition. Completed wrong-layout and unsafe-destination controls fail those checks.
The additional LCD-on request is rejected without a VRAM write. The full logical map is checked, including cells outside the visible viewport.
These results qualify the selected Emu198x 0.39.0 configuration and Asm198x 0.0.71 fixture. They do not establish original-hardware timing, stock-OS loading, interrupt coexistence or a whole-game frame budget. The complete caller keeps reset setup separate from reusable drawing. Copy counts above are not measured timing claims.