The screen, keyboard and mouse through traps

This M68K has no device at any address: the screen, the keyboard and the mouse are all trap #15 tasks. Drawing, colours, double buffering, key state and the pointer, each with a program you can run.

Some machines put their devices at addresses and a program reaches them with move. This one has none: there is no framebuffer to write into, no keyboard register to poll, no address anywhere in the 16 megabytes that is anything but memory. The screen, the keyboard and the mouse are all trap #15 tasks, one task per operation, and the request has the shape you already know: the task number in d0.b, the arguments in d1 and up.

The screen

The screen is 640 by 480 pixels, which is the size a program starts with and the smallest it can be set to. The origin is the top left, x grows right and y grows down, coordinates are pixels and drawing outside the screen is quietly ignored.

Two colours are kept for you: the pen, which draws lines, outlines, pixels and text, and the fill, which fills the insides of rectangles and ellipses. Each is one task, and the colour is a long written $00BBGGRR: blue in the high byte, then green, then red in the lowest, which is EASy68K's order and backwards from the #RRGGBB you write in CSS.

colourvaluecolourvalue
black$00000000red$000000FF
white$00FFFFFFlime$0000FF00
gray$00808080blue$00FF0000
yellow$0000FFFFaqua$00FFFF00

Press Run on this one and watch the Screen panel next to it.

A rectangle and an ellipse both take the same four numbers, the corners of a box: d1 and d2 are its left and top, d3 and d4 its right and bottom. The ellipse is the one inscribed in that box, so a square box draws a circle. Both exclude their right and bottom edges, the way the Windows drawing calls EASy68K was built on do, which means a box whose edges meet draws nothing at all.

Try changing move.l #480, d3 on the ellipse to move.l #400, d3 and running again: the circle becomes an egg, because the box stopped being square.

The drawing tasks

taskwhat it drawsreads
80set the pen colourd1.l = $00BBGGRR
81set the fill colourd1.l = $00BBGGRR
82one pixel in the pen colourd1.w = x, d2.w = y
83read a pixel's colourd1.w = x, d2.w = y, answers in d0.l
84a line, and the drawing point ends at its endd1.w, d2.w, d3.w, d4.w
85a line from the drawing point to hered1.w = x, d2.w = y
86move the drawing point without drawingd1.w = x, d2.w = y
87a filled rectangle, outlined with the pend1.w, d2.w, d3.w, d4.w
88a filled ellipse in that rectangled1.w, d2.w, d3.w, d4.w
89flood fill outwards from a pixeld1.w = x, d2.w = y
90the outline of a rectangle, nothing insided1.w, d2.w, d3.w, d4.w
91the outline of an ellipse, nothing insided1.w, d2.w, d3.w, d4.w
92the drawing moded1.b = 2, 4, 16 or 17
93the pen width in pixelsd1.b
94show the off screen image
95text at a pixel position, over what is therea1 = string, d1.w = x, d2.w = y
96where the drawing point isanswers d1.w = x, d2.w = y

Tasks 84, 85, 86 and 96 share one drawing point, which is where the next 85 starts from, so a polyline is one 86 and then one 85 per corner.

Task 92 takes four modes. 4 draws normally and is what a program starts in. 2 moves the drawing point and changes no pixel. 16 and 17 turn double buffering off and on. EASy68K's other modes, the ones that combine the new pixel with the old one bitwise, stop the program here with an error naming the mode.

Two more tasks belong to the screen without drawing on it. Task 11 moves the text cursor, which is where printed text lands, in character cells counted from the top left, and d1.w = $FF00 clears the whole screen, text and graphics together. Task 33 sets or reads the screen size, with the width in the high word of d1.l and the height in the low word, and d1.l = 0 asks instead of setting.

Text and graphics share one image here, because EASy68K had a single output window. So trap #15 task 14 both appends to the transcript above the screen and draws the string on the screen at the text cursor, and clearing with task 11 wipes the drawing too.

Double buffering

Drawing a moving picture straight onto the visible screen shows every half finished frame. Mode 17 sends the drawing to an off screen image instead, and task 94 shows it, so the reader only ever sees whole frames.

This one runs until you press Stop.

Task 23 is what makes it move at the same speed whatever your machine is doing: it lets two hundredths of a second of program time pass, and the editor stays responsive throughout, so Stop still answers and the screen still repaints. Take the delay out and the ball moves as fast as the instruction budget allows and then the program stops, which is not the same thing as fast.

Try changing move.b #17, d1 to move.b #16, d1, which turns double buffering off. The ball still moves and now it flickers, because you are watching the clear and the draw happen.

The keyboard

Two ways to read it. Typed characters come through the text tasks: task 7 says whether one is waiting, task 5 takes one, task 2 takes a whole line. Key state is different: task 19 asks whether up to four named keys are held down right now, which is what a game wants.

Task 19 takes four key codes packed into d1.l, one per byte, and answers in d1.l with one $FF or $00 byte per key, in the same order. So the highest byte of the answer belongs to the highest byte of the question.

The key codes are EASy68K's, and most of them you can work out:

  • A letter is the ASCII code of its capital, so A is $41 and Z is $5A, whether or not Shift is held.
  • A digit on the top row is its ASCII code, 0 is $30 and 9 is $39.
  • The function keys run from F1 at $70.
  • The arrows are left $25, up $26, right $27, down $28.

The rest, and there are thirty of them, are on the trap tasks documentation page.

Click the Screen panel before you press a key: the screen only gets the keyboard when it has the focus, and a ring around it says so while it does.

btst #24, d1 tests bit 24, which is the lowest bit of the highest byte, and a byte of $FF has that bit set while a byte of $00 does not. A key held down is reported at least once however briefly it was tapped, so a loop that polls every two hundredths of a second never misses one.

The mouse

Task 61 reads it, and d1.b says which reading you want: 0 for where the pointer is and which buttons are down now, 1 for the last button release, 2 for the last button press. The answer comes back in two registers:

  • d0.b is the buttons and modifiers, one bit each, from bit 6 down: Ctrl, Alt, Shift, Double, Middle, Right, Left. So bit 0 is the left button and bit 4 is Shift.
  • d1.l is the position, y in the high word and x in the low word, in screen pixels whatever the panel's zoom is.

The last press and the last release stay until the next one, so a program that polls slowly still sees every click.

lsr.l #8, d2 twice is how the y is brought down sixteen places, since a constant shift count is limited to 8. swap d2 and a mask would do the same in two instructions.

How much you can draw

Every trap #15 costs the simulator one instruction out of the two million a Playground is given, whatever the task does. So a drawing loop is counted in traps: a filled rectangle is one of them, and the same rectangle drawn with task 82 is one per pixel. Use the shape tasks, keep the work of a frame to a few dozen traps, and let task 23 set the pace.

Your turn

Fill a red rectangle over the box from (10, 10) to (100, 100), then read the colour of the pixel at (50, 50) back with task 83 and leave it in d0. Red is $000000FF, so d0 comes out at 255.

Show solution

The second one asks the screen how big it is with task 33 and takes the packed answer apart: the width in d1 and the height in d2, each on its own. A program that has not resized the screen gets 640 and 480.

Show solution