Skip to content

WIN320 — windowed UI

WIN320.DLL is a lightweight windowed UI library for Sprinter in mode #81 (320×256, 256 colours). It is not a framework: the library provides a set of ready-made building blocks — panels, buttons, edit fields, listboxes, scrollbars, progress bars, icons, checkboxes and radio buttons — from which an application in assembly, C (SDCC) or Turbo Pascal assembles an interface in the GFX Viewer / Flex Navigator style. Everything is drawn by the Sprinter hardware accelerator, but that is an implementation detail, not part of the interface.

The shared DLL loading rules are in the libraries overview.

  • Declarative — controls are described by structures, references to them are collected into a WinItem[] array, the array into a WinWindow. The window is drawn with a single win_draw call and then updated selectively: the application changes a control’s state, sets the WIN_IT_DIRTY flag and calls win_update — only the changed items are redrawn.
  • Imperative — the same structures are passed directly to win_button, win_panel, win_label and so on, without a window or arrays.

The structures are identical on both paths: a button descriptor from a window list works unchanged in a direct call.

include "win320.inc"
ld bc,#0050 ; DSS SETVMOD
ld a,#81 ; the application enables mode 320x256x256
rst #10
ld hl,dll_name ; "WIN320.DLL",0
ld a,3 ; DLL — into window WIN3
call LIBMAN.l_load
jp c,load_error
ld (dll_handle),hl ; the font is already loaded from the DLL tail
; GUI palette + clear the screen with the desktop colour
ld e,WIN_STYLE_PALETTE|WIN_STYLE_CLEAR
ld d,#FF ; #FF = desktop colour from the theme
ld b,WIN_STYLE
call api
jr nz,win_error
...
api: ld hl,(dll_handle)
call LIBMAN.l_call ; arguments: A, DE, IX, IY
ret c ; CF — libman dispatcher error
or a ; ZF from the WIN320 status
ret

Walkthrough: the application enables the mode itself, loads the DLL into WIN3 (code, data and stack are best kept in WIN0–WIN2) and calls win_style first of all — it installs the GUI palette and fills the screen. Without the palette every colour comes out black, so this call is mandatory. The api helper is a typical idiom: the two error levels (the dispatcher’s CF and the status in A; 0 — success, #20..#28WIN_ERR_* codes) are checked in one place.

Every drawable structure starts with the same header: x, y, width, height. Coordinates are measured from the origin — the screen corner by default, or the window corner while a window is being drawn. A dialog is therefore moved by editing the two WinWindow.x/y fields, with no control coordinates to recalculate.

Colours are almost never given explicitly: the value #FF in any colour field means “take it from the theme”. The theme is 18 colour roles (light and shadow of the 3D bevels, panel face, desktop background, text, edit field, selection, window title…) set with a single win_set_theme call — and the whole interface is recoloured.

window: dw 32,20,256,216 ; x, y, width, height
db #FF ; body colour — from the theme
db 0 ; window flags
db 7 ; number of items
db 2 ; index of the focused item
dw items ; WinItem[] array
dw title ; title (0 — no title bar)
db #FF ; title colour — from the theme
db 0 ; internal field
items: ; 8 bytes per entry: type, flags, id, pointer, user_data
db WIN_T_BUTTON, WIN_IT_HIT|WIN_IT_FOCUSABLE|WIN_IT_PRESS
db ID_OK
dw ok_button ; -> WinButton structure
dw 0
...

win_draw renders the whole window; win_update redraws only entries carrying the WIN_IT_DIRTY flag (and clears it). The typical loop: handle an event → change the control’s state → set dirty → win_update.

A window may have a title bar with a close button (WinWindow.title plus the WIN_WND_CLOSE flag) — the strip is drawn automatically, and a full click on the close button arrives as the WIN_EV_CLOSE event.

win_open saves the background under the window into memory pages allocated by the application (win_set_backstore) and draws the window; win_close restores the background. The stack holds up to four nested windows, strictly last-opened-first-closed. On an error in the item list win_open rolls back atomically — no half-drawn dialog is left on screen.

Two polling styles, both through a WinTrack structure owned by the application:

  • win_poll — one non-blocking iteration: no event means WIN_EV_NONE, and the application can do background work;
  • win_track — blocks until an event (with the WIN_TRK_HALT flag it waits economically via HALT).

Events: left/right clicks, auto-repeat, hover/leave, hotkeys from a WinKey[] table, arbitrary keys, focus changes, checkbox/radio changes, a click outside all objects, and the window close button. Along with the event come the item index and id, the sub-area (e.g. a scrollbar arrow or a listbox row) and the mouse coordinates.

Keyboard focus is handled by the library: Tab/Shift+Tab cycle through items with the WIN_IT_FOCUSABLE flag, Enter presses the focused button, Space toggles a checkbox, arrows move within a radio group.

The mouse driver is initialised by the application (BIOS RST #30, C=0) — the library only reads it and manages the cursor.

  • Edit field (win_edit) — modal editing directly in the application’s buffer: caret, Backspace/Delete, Home/End, word jumps with Ctrl+←/→, Esc rollback, click-to-position, password mode.
  • Listbox — rows either as an array of pointers or in memory pages (up to 5461 entries); redrawing is differential, a one-row shift uses fast hardware scrolling.
  • Scrollbar — recomputes its thumb from first/visible/total itself; the arrows are serviced by the control, hit-testing reports the sub-area.
  • Progress bar — the unit is a percent; only the difference is redrawn.
  • Icons — 8×8 and 16×16 from the application’s memory pages, with #FF hardware transparency; the winiconpack.py packer builds them from PNG/BMP.
  • Checkbox and radio buttons — state, group exclusivity, mouse and keyboard reactions all live inside the library; the application receives a ready-made WIN_EV_CHANGE.
static win_button_t ok = {40, 154, 72, 20, 0xff, 0, 0}; /* x,y,w,h,attr,flags,text */
static win_item_t items[7];
static win_window_t window = {32, 20, 256, 216, 0xff, 0, 7, 2, 0, 0xff, 0};
static win_track_t track;
win320_bind(handle); /* attach wrappers to the DLL */
win320_set_text_format(WIN_TXT_ASCIIZ);
win320_draw(&window); /* render the whole window */
for (;;) {
if (win320_poll(&track)) break; /* ABI error */
if (track.event == WIN_EV_NONE) continue;/* background work here */
if (track.id == ID_CANCEL) break;
items[STATUS_INDEX].flags |= WIN_IT_DIRTY;
win320_update(&window, &drawn); /* redraw only dirty items */
}

The package implements the same dialog three times — dialog.asm, dialog.c and DIALOG.PAS — convenient for comparing the languages. Turbo Pascal gets records and LibCall helpers, and the string format can be switched to “length byte” with win_set_text_format, so edit fields work directly with string[n].

  • The application enables mode #81 and switches the visible screen itself; the library can draw into either of the two buffers (win_set_screen).
  • Call data must not live in the DLL’s window: during a call the library itself is mapped there. Mutable structures (the window, the edit buffer, WinTrack) belong in writable RAM; in C they must not be const.
  • The library is not reentrant: do not call it from interrupt handlers.
  • Do not print through the DSS console while the graphics mode is active (see the overview).
File Purpose
WIN320.DLL the library; the font is embedded in the file tail
win320.inc constants and field offsets for sjasmplus
bind/win320.h, bind/win320.lib header and wrappers for C (SDCC)
bind/WIN320.INC records and helpers for Turbo Pascal
examples/dialog.asm, dialog.c, DIALOG.PAS one dialog in three languages
tools/winiconpack.py icon packer for PNG/BMP
WIN320.EXE visual test; applications do not need it