The editor lives in the same source as Bedlam, and it's switched on by one word. In 1995 BEDEDIT.ASM was BEDLAM.ASM with that word changed:
_editflag dw -1
BEDEDIT.ASM on this page is that complete source, the game and the editor. The BEDLAM.ASM on the Bedlam page has had the editor taken out.
With the flag set, start-up also checks for a Microsoft-compatible mouse driver, and instead of the intro and the game, the main routine runs the editor until Esc is pressed. Everything else is shared: the memory map, the interrupt hooks, the sprites, the levels and the drawing code are all the game's.
[CHECK: what the editor was for, and how far I meant to take it]
Three windows
The screen is a desktop with three windows on it: CHARACTERS, which shows the sprite sheet; MAP, which shows the level; and SPRITE EDITOR, which was going to be where sprites were drawn. The desktop is the intro screen's grey background, tiled.
Each window is a record of exactly 32 bytes, and the three records sit one after another. The labels for the first window's fields double as the field names for all of them: _window_x1 is the address of the first window's x1, so _window_x1[bx] is the x1 of whichever window starts at offset bx, which is 0, 32 or 64.
_worder dw 32, 0, 64
_window_x1:
_charwin_x1 dw 10
_window_y1:
_charwin_y1 dw 13
_window_x2:
_charwin_x2 dw 277
_window_y2:
_charwin_y2 dw 162
_window_off1:
_charwin_off1 dw 10+13*320
_window_off2:
_charwin_off2 dw 277+162*320
_window_w:
_charwin_w dw 268
_window_h:
_charwin_h dw 150
_window_proc:
_charwin_proc dw offset cs: charwin_proc
_window_len:
_charwin_len db 10
_window_title:
_charwin_title db 'CHARACTERS...'
_worder is the stacking order, front to back, as record offsets. It starts as 32, 0, 64: the map in front, then the characters, then the sprite editor.
Messages
Each window has a handler, and the editor talks to it with a message number in AX, the way Windows does. 0 means redraw yourself, and 10h means the left button went down, with the mouse position already made relative to the window in CX and DX. Room was left for button-up and right-button messages, but those lines are commented out.
A handler is called through its record, and like Bedlam's timer routines it has to start with two NOPs. The caller checks for 9090h at the address before jumping.
Drawing the screen
The editor redraws everything, every time round its loop: the desktop, then the three windows from back to front, then the mouse pointer, then a copy to the screen after the vertical retrace. Drawing back to front is all the overlapping needs. Whatever is drawn last is on top, so there's no clipping and no record of what's covering what.
The window furniture is built from a few routines. edit_box draws a raised box, edit_frame an outline in one colour, edit_rect a filled rectangle, and edit_button copies one of eight 9x9 button bitmaps: Close, Under, Left, Right, Up, Down, Resize and Blank. A title bar is a Close button, a raised box and an Under button. The title strings are in the records but never drawn.
The CHARACTERS window copies the sprite sheet straight out of the sprite segment, column by column. The MAP window draws the level at a quarter size, four pixels by four for each square, from a little table of 4x4 patterns, one per tile type. And then it calls the game's own rules:
inc si
add di, 4
loop mapwin_draw_xloop
pop cx
pop di
add di, 320*4
dec dx
jnz mapwin_draw_yloop
call game_move
So the level in the map is alive: rocks fall, bats fly round their walls, and because the editor redraws as fast as the screen refreshes, it all runs a lot faster than the game does.
The mouse
The mouse driver does the hard part, through int 33h. Function 3 returns the position and the buttons. The driver's coordinates are twice the screen's, so the editor halves both to get pixels:
mov ax, 0003h
int 33h
shr cx, 1
shr dx, 1
mov _mouse_b, bx
mov _mouse_x, cx
mov _mouse_y, dx
The driver's own pointer doesn't work with a back buffer, so the editor draws its own. The pointer is 13x20 pixels with an AND mask and an XOR mask, drawn the same way as Bedlam's sprites. Before drawing, it saves the pixels underneath, so mouse_undo can put them back. A shadow under the pointer was written and then commented out.
A left click goes to the front window first. If the click is inside its rectangle, its handler gets the message, if not the next window is tried, and if no window was hit the click was on the desktop:
; Test top window first.
mov bx, _worder[0]
cmp cx, word ptr _window_x1[bx]
jb edit_main_lbutton_notwin1
cmp cx, word ptr _window_x2[bx]
ja edit_main_lbutton_notwin1
cmp dx, word ptr _window_y1[bx]
jb edit_main_lbutton_notwin1
cmp dx, word ptr _window_y2[bx]
ja edit_main_lbutton_notwin1
Dragging and stacking
A click on a title bar, between the two buttons, starts a drag. First the mouse is limited with int 33h functions 7 and 8, so that the pointer can only go where the window would still be fully on the screen. Then the drag loop reads the mouse, adds how far it moved to the window's corners, works out their screen offsets again, and redraws the whole screen, until the button is let go. Then the limit goes back to the whole screen.
A click anywhere else in a window brings it to the front. The window's handler looks for its own offset in _worder and swaps it one place forward, twice, so wherever it was it ends up first:
cmp _worder[4], 0
jne charwin_ldown_nosort1
mov ax, _worder[2]
mov _worder[4], ax
mov _worder[2], 0
charwin_ldown_nosort1:
cmp _worder[2], 0
jne charwin_ldown_nosort2
mov ax, _worder[0]
mov _worder[2], ax
mov _worder, 0
charwin_ldown_nosort2:
Each window has its own copy of this code, with its own offset in it.
What isn't finished
The windows open, drag and stack, and that's as far as it got. What's missing:
- None of the buttons do anything. Close and Under are drawn on every title bar, and the scroll bar buttons on the CHARACTERS window and the two buttons on the map's info bar are just pictures.
- Clicking in the map doesn't change the level, and clicking a sprite doesn't pick it.
- The SPRITE EDITOR window is an empty box.
- The right button is read but ignored.
- There's no way to save, so nothing could be written back to BEDLAM.BED.
- The window titles are never drawn.
- The map runs the game while you edit, which would need a pause.
There's also an empty editor routine further down the source, with its own logon, main and logoff that each just return.
The command line
Twelve days later, on 30 June 1995, I started BEDPARAM.ASM, a separate program for trying out command-line switches. It lowercases the command tail, finds each slash, and matches the word after it against a table:
_params db '?', 00h ; Help.
_param_help db 'help', 00h ; Help.
_param_dev db 'dev=', 00h ; Development mode.
_param_new db 'new=', 00h ; Create new .BED file.
_param_edit db 'edit=', 00h ; Edit existing .BED file.
_param_play db 'play=', 00h ; Play .BED file (by default BEDLAM.BED).
The comments describe one program for everything: /play= to play a .BED file, /edit= to edit one, and /new= to start a new one. Each switch only prints its name so far. In BEDLAM.ASM the call to it is there, commented out, next to a _devflag that nothing reads yet.
Source 2 files
- BEDEDIT.ASM Assembly · 6414 lines · the game and the editor
- BEDPARAM.ASM Assembly · 509 lines · the command-line test
Downloads
- bedlam-editor.jsdos js-dos bundle