The next series of posts will document a windowing system I built for the Atari 8 bit computer using Action!. In this first post I’ll show you the windowing system is designed, and show you the first few functions to get started.
To keep the windowing system somewhat simple, I chose not to include any Z ordering, and instead put window order management on the programmers shoulders. Programs are small enough on the Atari that this isn’t really a problem as far as I am concerned.
Design
The Window system needs a few basic things. The first two:
- Window handles which hold the information about each window (top and left cursor position, width, height, etc)
- Memory to store the screen contents the window replaces when drawn.
I chose a simple stack based ordering system. This means windows are added to a stack as they are created. The last one added, should be the first one removed. There are a number of reason I chose this. First and foremost was simplicity. This approach brings with it less management of window screen buffer memory. It also helps keep the code size down.
I gave each window handle 8 attributes:
- Status (bS)
- X position / column (bX)
- Y position / row (bY)
- Width (bW)
- Height (bH)
- Inverse flag (bI)
- Window Buffer Memory Location (cM)
- Window Buffer Memory Size (cZ)
To make the windowing system easy to use in programs I broke it into library modules that fit in with the other library routines I’ve posted about before. These are, and should be included in this order in any program:
- DEFWIN.ACT
- LIBWIN.ACT
Next I’ll break down whats in each one.
DEFWIN.ACT
To make storage, almost straight forward, I created a record:
; Window handle type definition TYPE WnREC=[BYTE bS,bX,bY,bW,bH,bI CARD cM,cZ]
The BYTE parameters each take 1 byte of memory to store. The CARD parameters each take 2 bytes of memory to store. This brings the total record size to 10 bytes. There is a definition for the window handle record size. While setting up definitions, I also setup the maximum size for the window screen buffer memory size. Just under 3K. You can make this bigger or smaller as needed:
; Record and memory alloc sizes DEFINE WRECSZ="10" DEFINE WBUFSZ="2968"
Next comes the system-wide pointers used for accessing the window handles and window screen buffer memory:
; Pointers for window handle and mem WnREC POINTER pWn CARD POINTER cpWM
Then the memory is allocated for both the window handles and the window screen buffer memory. I set aside enough window handle memory for 11 handles – 10 for general usage, and 1 as a system handle (for dedicated functions that will come later). You can shrink the size of the window handle memory to fit your needs. It just needs to be a multiple of the WRECSZ defined previously:
; Window handle and memory storage ; 10 handles + 1 system handle BYTE ARRAY baW(110),baWM(WBUFSZ)
I also define the vector to screen memory, which is locations 88 and 89. Created in this way, the computation of the actual address in memory is done for you by Action!:
; Screen memory vector CARD RSCRN=88
The window system also keeps track of where the cursor is, virtually. To do so I used a two byte record to hold the virtual X and Y positions. Then assign it as a variable for use system wide:
; Virtual cursor type definition TYPE WnPOS=[BYTE vX,vY] ; Virtual cursor var WnPOS vCur
There are some other defines in the file which are use to make program source code easier to read. These are:
; Background DEFINE WBNONE="0" ; Inverse Flags DEFINE WINVON="1" DEFINE WINVOFF="0" ; Window Status DEFINE WSFREE="0" DEFINE WSUSED="1" ; Positioning DEFINE WPABS="128" DEFINE WPCENT="255" ; Error Status DEFINE WERNONE="100" DEFINE WERNOPN="101" DEFINE WERUSED="102"
The Inverse flags are WINVON and WINVOFF. They represent the values used to set a window to be drawn in inverse video or not.
The Window Status flags are WSFREE and WSUSED. They represent the values assigned to the window handles status byte. 0, the window handle is free (available). 1, the window handle is in use.
The Error Status flags are:
- WERNONE – Window ERror NONE – No window handles available
- WERNOPN – Window ERror Not OPeN – Window handle not open
- WERUSED – Window ERror USED – Window handle in use
The other definitions will be discussed when the time comes.
LIBWIN.ACT
This contains all the window routines. For this post, I will cover the initialization routine and the open routine. Future posts will cover other routines.
Init (WInit)
First the init routine. The window system needs to be initialized before use. The initialization routine is described here.
- First, it turns the cursor off, sets the left margin to 0, moves the physical cursor to top left corner, then sends clear screen to CIO.
- Then it zeros out the window screen buffer memory, and sets the initial index to the memory which will be at byte 0 of the window memory address.
- Then for each of the 10 general window handles and 1 system window handle, it sets the window handle attributes to status of free, inverse off, base memory address of window screen buffer memory, and 0 for all other attributes.
- Last it sets the virtual cursor coordinates.
; -------------------------------------- ; Proc..: WInit() ; Desc..: Initializes windowing system. ; -------------------------------------- PROC WInit() BYTE bL ; Setup cursor and screen Poke(752,1) Poke(82,0) Position(0,0) Put($7D) ; Clear window memory Zero(baWM,WBUFSZ) ; Set index into window memory cpWM=baWM ; Work on 10 window+system handles for bL=0 to 10 DO ; Find windows handle location pWn=baW+(WRECSZ*bL) ; Clear handle record vars pWn.bS=WSFREE pWn.bX=0 pWn.bY=0 pWn.bW=0 pWn.bH=0 pWn.bI=WINVOFF pWn.cM=baWM ; point at base storage pWn.cZ=0 OD ; Set virtual cursor coords vCur.vX=0 vCur.vY=0 RETURN
Open (WOpen)
Now for the window open routine. To open a window, WOpen is called 5 parameters:
- x position / column
- y position / row
- width (inclusive of border)
- height (inclusive of border)
- inverse flag
The routine will return either the window handle number assigned, or an error code. The window handle number is chosen based on the first free handle. What the routine does:
- Sets the default return code to no window handles available (WERNONE).
- It cycles through each general window handle starting at the first, looking for the first one that is not used. To do this, it finds the window handle in memory, and checks the status byte.
- If the window handle is free, the window handle status byte is set to used.
- It sets the window handle memory location pointer to the same value as the systems window screen buffer memory pointer. The system one gets updated as the window is drawn and underlying screen elements are saved to it. It also sets the size of the memory needed to store the underlying screen elements, which is the width times height of the window.
- It sets the other window handle attributes to match those passed in to the function.
- It then computes the location in screen memory where the top left of the window location should be.
- It draws the window. To do this, it creates each row of the window from top to bottom. This is done by creating an in-memory image of each row, then copying that memory block into screen memory at the correct location. It will also inverse the in-memory image of the row if the inverse flag was set.
- Then it sets the return value of the function to the window handle that was used.
- Last, the routine exits passing back the return value which is the window handle that was used.
; -------------------------------------- ; Func..: BYTE WOpen(BYTE x,y,w,h,i) ; Desc..: Opens new window ; Params: x = column ; y = row ; w = width ; h = height ; i = inverse ; WINVON/WINVOFF ; Return: Window handle number ; >100 on error ; -------------------------------------- BYTE FUNC WOpen(BYTE x,y,w,h,i) BYTE bR,bL,bD CHAR ARRAY cL(40) CARD POINTER cS ; Set default return code bR=WERNONE ; Cycle through handles (excl system) for bL=0 to 9 DO ; Find window handle record pWn=baW+(WRECSZ*bL) ; If handle record not in use if pWn.bS=WSFREE then ; Set handle record in use pWn.bS=WSUSED ; Set storage address and size pWn.cM=cpWM pWn.cZ=w*h ; Set other handle record vars pWn.bX=x pWn.bY=y pWn.bW=w pWn.bH=h pWn.bI=i ; Find top left corner of window ; in screen memory cS=RSCRN+(y*40)+x ; Draw window for bD=0 to h-1 DO ; Build window line as string ; Top or bottom line "+-+" if bD=0 or bD=h-1 then SetBlock(cL,w,82) ; Top line corners if bD=0 then cL(0)=81 cL(w-1)=69 ; Bottom line corners else cL(0)=90 cL(w-1)=67 fi ; Middle line "| |" else SetBlock(cL,w,0) cL(0)=124 cL(w-1)=124 fi ; If inverse flag, flip line if i=WINVON then StrInv(cL) fi ; Save underlying scrn to win mem MoveBlock(cpWM,cS,w) ; Inc mem ptr index by width cpWM==+w ; Move line to screen MoveBlock(cS,cL,w) ; Inc scr by 40 to next line start cS==+40 OD ; Set return to handle number bR=bL ; Exit loop EXIT fi OD RETURN(bR)
Usage
The following program demonstrates the usage. This program will be built upon as the posts progress. You’ll notice LIBSTR.ACT is included as well. It has the StrInv function which is used to inverse a window.
; Program: WINDOW.ACT ; Author.: Wade Ripkowski ; Date...: 2016.04 ; Desc...: Test Windowing Library ; License: Creative Commons ; Attribution-NonCommercial- ; NoDerivatives ; 4.0 International ; Include library INCLUDE "D3:DEFWIN.ACT" INCLUDE "D3:LIBSTR.ACT" INCLUDE "D3:LIBWIN.ACT" ; Start MODULE PROC Main() ; Window handles BYTE bW1,bW2,bW3 ; Init Window System WInit() ; Open window 1 bW1=WOpen(10,5,20,6,WINVOFF) ; Open window 2 bW2=WOpen(5,15,30,6,WINVON) ; Open window 3 bW3=WOpen(15,8,10,8,WINVOFF) RETURN
Result
That’s it. The next post will cover window closing and background. Other posts will include window titles, printing to windows, putting characters to windows, and eventually some widgets.