konilo

Check-in [3b74cc59ae]
Login

Check-in [3b74cc59ae]

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:ilo+graphica.cs: design optimizations ported from ilo-x
Downloads: Tarball | ZIP archive
Timelines: family | ancestors | trunk
Files: files | file ages | folders
SHA3-256: 3b74cc59aebd3382ba0e5d01240e255148e09cab4b43a377634bbc43ec207bc1
User & Date: crc 2025-12-15 16:37:28.152
Context
2025-12-15
16:37
ilo+graphica.cs: design optimizations ported from ilo-x Leaf check-in: 3b74cc59ae user: crc tags: trunk
2025-12-12
15:08
ilo-wayland: new implementation in C check-in: d7f75480e6 user: crc tags: trunk
Changes
Unified Diff Ignore Whitespace Patch
Changes to RELEASE.txt.
130
131
132
133
134
135
136




137
138
139
140
141
142
143
144
145
146
147





148
149
150
151
152
153
154

  - new implementation (oberon; for obcn)

- ilo.st

  - new implementation (smalltalk; for gnu smalltalk)





- ilo-x.c

  - double buffering
  - optimizations to drawing process

- milo-x.c

  - double buffering
  - optimizations to drawing process
  - separate display buffers for each session






IMAGE

- dictionary data now in konilo.pali instead of being built by
  separate preprocessor

BLOCKS








>
>
>
>











>
>
>
>
>







130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163

  - new implementation (oberon; for obcn)

- ilo.st

  - new implementation (smalltalk; for gnu smalltalk)

- ilo-wayland.c

  - new implementation (c, for wayland)

- ilo-x.c

  - double buffering
  - optimizations to drawing process

- milo-x.c

  - double buffering
  - optimizations to drawing process
  - separate display buffers for each session

- ilo+graphica.cs

  - optimizations to drawing process
  - faster keyboard input

IMAGE

- dictionary data now in konilo.pali instead of being built by
  separate preprocessor

BLOCKS

Changes to ilo-vm/ilo+graphica.cs.
1
2
3
4
5
6
7
8
9
10

11
12
13
14
15
16
17
18
19
20
21
22
23
24
25










26
27
28


29


30
31
32
33

34
35
36
37


38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61



62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78



79



80

81
82







83



















































































84
85
86



87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123





124
125
126






127
128
129
130
131
132
133
134


135
136

137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153

154
155
156
157



158
159



160
161
162
163

164


165
166
167
168

169
170
171
172

173
174
175


176
177


178
179

180
181








182
183
184
185
186
187
188
189
190
191
192
193
194
195
196



197
198
199
200
201
202
203
204
205
206

207

208

209
210
211
212
213
214
215
216

217
218

219
220
221
222

223
224
225
226
227
228
229
230
231
232







233
234
235
236
237
238
239



240





241
242
243
244
245
246
247
// ilo+graphica.cs, (c) charles childers

using System.Drawing;
using System.Windows.Forms;
using System;
using System.IO;
using System.Text;
using System.Collections.Generic;
using System.Drawing.Imaging;
using System.Threading;


namespace ilo
{
  class VM
  {

    Form window;
    PictureBox display;
    Bitmap framebuffer;
    Graphics gfx;
    public int sp, rp, ip;
    public int[] d, a, m;
    private Bitmap bitmap;

    private char? keyBuffer = null;











    private void Form_KeyPress(object sender, KeyPressEventArgs e)
    {


      keyBuffer = e.KeyChar == '\r' ? '\n' : e.KeyChar;


    }

    public int WaitKey()
    {

      while (true)
      {
        if (keyBuffer.HasValue)
        {


            char ch = keyBuffer.Value;
            keyBuffer = null;
            return ch;
        }

        Application.DoEvents();
      }
    }

    public VM()
    {
      sp = 0;
      rp = 0;
      ip = 0;
      d = new int[33];
      a = new int[257];
      m = new int[65536];
      load_rom();
      bitmap = new Bitmap(640, 480);
      setup_window();
    }

    public void setup_window()
    {



      window = new Form();
      window.Text = "ilo VM Display";
      window.Width = 670;
      window.Height = 530;
      framebuffer = new Bitmap(640, 480);
      gfx = Graphics.FromImage(framebuffer);
      gfx.Clear(Color.Black);
      display = new PictureBox();
      display.Dock = DockStyle.Fill;
      display.Image = framebuffer;
      display.SizeMode = PictureBoxSizeMode.CenterImage;
      display.BackColor = Color.DarkBlue;
      display.Paint += Display_Paint;
      window.Controls.Add(display);
      window.KeyPreview = true;
      window.KeyPress += Form_KeyPress;
      window.FormClosed += (sender, e) => Environment.Exit(0);



      var thread = new System.Threading.Thread(() => Application.Run(window));



      thread.Start();

    }








    private object framebufferLock = new object();




















































































    public void put_pixel(int x, int y, int color)
    {



      lock(framebufferLock)
      {
        if (x >= 0 && x < 640 && y >= 0 && y < 480)
        {
          Color pixelColor = (color != 0) ? Color.White : Color.Black;
          window.Invoke((MethodInvoker)(() =>
          {
            framebuffer.SetPixel(x, y, pixelColor);
          }));
        }
      }
    }

    public int get_pixel(int x, int y)
    {
      lock(framebufferLock)
      {
        if (x >= 0 && x < 640 && y >= 0 && y < 480)
        {
          Color color = Color.Black;
          window.Invoke((MethodInvoker)(() =>
          {
            color = framebuffer.GetPixel(x, y);
          }));
          return (color.ToArgb() == Color.Black.ToArgb()) ? 0 : 1;
        }
        else
        {
          return 0;
        }
      }
    }

    private void Display_Paint(object sender, PaintEventArgs e)
    {
      int imageX = (display.Width - framebuffer.Width) / 2;
      int imageY = (display.Height - framebuffer.Height) / 2;






      using (Pen pen = new Pen(Color.Red, 2))
      {






        int cursorX = imageX + tx;
        int cursorY = imageY + ty;
        e.Graphics.DrawRectangle(pen, cursorX, cursorY, FONT_W, FONT_H);
      }
    }

    public void clear_screen()
    {


      gfx.Clear(Color.Black);
      display.Invalidate();

    }

    const int FONT_W = 8;  // Font width
    const int FONT_H = 16; // Font height
    const int FW = 640;    // Screen width
    const int FH = 480;    // Screen height
    int tx = 0;            // Cursor x position
    int ty = 0;            // Cursor y position

    byte Reverse(byte b)
    {
      b = (byte)((b & 0xF0) >> 4 | (b & 0x0F) << 4);
      b = (byte)((b & 0xCC) >> 2 | (b & 0x33) << 2);
      b = (byte)((b & 0xAA) >> 1 | (b & 0x55) << 1);
      return b;
    }


    void HandleSpecialCharacter(int c)
    {
      if (c == 8 || c == 127) // BACKSPACE/DELETE
      {



        if (tx - FONT_W >= 0)
        {



          tx -= FONT_W;
          Dputc(' '); // Output space to erase character
          tx -= FONT_W;
          Redraw();

        }


      }
      else if (c == 9) // TAB
      {
        int ts = tx / FONT_W;

        for (int i = ts; i < ((ts + 7) / 8) * 8; i++)
        {
          Dputc(' ');
        }

      }
      else if (c == 10 || c == 13) // CR or LF
      {


        ty += FONT_H;
        tx = 0;


        Redraw();
      }

    }









    void HandleRegularCharacter(byte[] bitmap)
    {
      for (int x = 0; x < FONT_H; x++)
      {
        for (int y = 0; y < FONT_W; y++)
        {
          int set = Reverse(bitmap[x]) & (1 << y);
          put_pixel(tx + y, ty + x, set != 0 ? -1 : 0);
        }
      }
      tx += FONT_W;
    }

    void AdvanceCursorAndScroll()
    {



      if (tx >= FW)
      {
        tx = 0;
        ty += FONT_H;
      }
      if (ty > (FH - FONT_H))
      {
        ty -= FONT_H;
        Scroll();
      }

    }



    public void Scroll()
    {
      lock(framebufferLock)
      {
        window.Invoke((MethodInvoker)(() =>
        {
          using(Graphics g = Graphics.FromImage(framebuffer))
          {

            g.DrawImage(framebuffer, new Rectangle(0, 0, FW, FH - FONT_H),
              new Rectangle(0, FONT_H, FW, FH - FONT_H),

              GraphicsUnit.Pixel);
            g.FillRectangle(Brushes.Black, 0, FH - FONT_H, FW, FONT_H);
          }
        }));

      }
    }

    void Redraw()
    {
      display.Invalidate();
    }

    void Dputc(int c)
    {







      int index = c * ((FONT_H * FONT_W) / 8);
      byte[] bitmap = new byte[FONT_H];

      Array.Copy(font, index, bitmap, 0, FONT_H);
      HandleSpecialCharacter(c);
      if (!(c == 8 || c == 127 || c == 9 || c == 10 || c == 13))
      {



        HandleRegularCharacter(bitmap);





      }
      AdvanceCursorAndScroll();
    }

    private int[] acc = new int[32];
    private int acc_i = 0;
    private int on = 0;










>









<


<


>
>
>
>
>
>
>
>
>
>



>
>
|
>
>




>
|

|

>
>
|
|
|
<
<
<












<





>
>
>
|
|
|
|
|
<
<
|
|
|
|
|
|
|
|
|
|
>
>
>
|
>
>
>

>


>
>
>
>
>
>
>
|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>



>
>
>
|

|
<
|
<
<
|
<
<





<
<
|
<
|
<
<
<
<
<
<
|
|
|
<







>
>
>
>
>



>
>
>
>
>
>
|
|






>
>
|
|
>

















>
|



>
>
>
|
|
>
>
>

|
<
|
>

>
>



|
>




>



>
>
|
|
>
>
|

>


>
>
>
>
>
>
>
>
|






|


<




>
>
>
|
|
|
|
|
|
|
|
|
|
>
|
>
|
>


|

<
<
<
<
>
|
|
>
|
<
<
<
>





|




>
>
>
>
>
>
>




|
|

>
>
>
|
>
>
>
>
>







1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

21
22

23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56



57
58
59
60
61
62
63
64
65
66
67
68

69
70
71
72
73
74
75
76
77
78
79
80
81


82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202

203


204


205
206
207
208
209


210

211






212
213
214

215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280

281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327

328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353




354
355
356
357
358



359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
// ilo+graphica.cs, (c) charles childers

using System.Drawing;
using System.Windows.Forms;
using System;
using System.IO;
using System.Text;
using System.Collections.Generic;
using System.Drawing.Imaging;
using System.Threading;
using System.Runtime.InteropServices;

namespace ilo
{
  class VM
  {

    Form window;
    PictureBox display;
    Bitmap framebuffer;

    public int sp, rp, ip;
    public int[] d, a, m;


    private char? keyBuffer = null;
    private readonly AutoResetEvent keyEvent = new AutoResetEvent(false);
    private readonly object keyLock = new object();

    private readonly object pixelLock = new object();
    private readonly object cursorLock = new object();
    private byte[] pixelBuffer = new byte[640 * 480];
    private int[] blitBuffer = new int[640 * 480];
    private bool hasDirty = false;
    private int dirtyMinX = 0, dirtyMinY = 0, dirtyMaxX = 0, dirtyMaxY = 0;
    private volatile bool cursorDirty = true;

    private void Form_KeyPress(object sender, KeyPressEventArgs e)
    {
      lock (keyLock)
      {
        keyBuffer = e.KeyChar == '\r' ? '\n' : e.KeyChar;
      }
      keyEvent.Set();
    }

    public int WaitKey()
    {
      keyEvent.WaitOne();
      lock (keyLock)
      {
        if (!keyBuffer.HasValue)
        {
          return 0;
        }
        char ch = keyBuffer.Value;
        keyBuffer = null;
        return ch;



      }
    }

    public VM()
    {
      sp = 0;
      rp = 0;
      ip = 0;
      d = new int[33];
      a = new int[257];
      m = new int[65536];
      load_rom();

      setup_window();
    }

    public void setup_window()
    {
      ManualResetEvent uiReady = new ManualResetEvent(false);
      var thread = new System.Threading.Thread(() =>
      {
        window = new Form();
        window.Text = "ilo VM Display";
        window.Width = 670;
        window.Height = 530;
        framebuffer = new Bitmap(640, 480);


        display = new PictureBox();
        display.Dock = DockStyle.Fill;
        display.Image = framebuffer;
        display.SizeMode = PictureBoxSizeMode.CenterImage;
        display.BackColor = Color.Black;
        display.Paint += Display_Paint;
        window.Controls.Add(display);
        window.KeyPreview = true;
        window.KeyPress += Form_KeyPress;
        window.FormClosed += (sender, e) => Environment.Exit(0);
        MarkFullDirty();
        SetupRenderTimer();
        uiReady.Set();
        Application.Run(window);
      });
      thread.IsBackground = true;
      thread.SetApartmentState(ApartmentState.STA);
      thread.Start();
      uiReady.WaitOne();
    }

    private void SetupRenderTimer()
    {
      var timer = new System.Windows.Forms.Timer();
      timer.Interval = 16;
      timer.Tick += (sender, args) => FlushFramebuffer();
      timer.Start();
    }

    private void EnsureBlitBuffer(int needed)
    {
      if (blitBuffer.Length < needed) blitBuffer = new int[needed];
    }

    private void MarkDirtyNoLock(int x, int y)
    {
      if (!hasDirty)
      {
        dirtyMinX = dirtyMaxX = x;
        dirtyMinY = dirtyMaxY = y;
        hasDirty = true;
        return;
      }
      if (x < dirtyMinX) dirtyMinX = x;
      if (x > dirtyMaxX) dirtyMaxX = x;
      if (y < dirtyMinY) dirtyMinY = y;
      if (y > dirtyMaxY) dirtyMaxY = y;
    }

    private void MarkFullDirtyNoLock()
    {
      hasDirty = true;
      dirtyMinX = 0;
      dirtyMinY = 0;
      dirtyMaxX = FW - 1;
      dirtyMaxY = FH - 1;
    }

    private void MarkFullDirty()
    {
      lock (pixelLock)
      {
        MarkFullDirtyNoLock();
      }
    }

    private void WriteDirtyRegionToBitmap(Rectangle rect)
    {
      int width = rect.Width;
      int height = rect.Height;
      int needed = width * height;
      EnsureBlitBuffer(needed);
      int idx = 0;
      for (int y = 0; y < height; y++)
      {
        int srcRow = (rect.Y + y) * FW + rect.X;
        for (int x = 0; x < width; x++)
        {
          blitBuffer[idx++] = pixelBuffer[srcRow + x] != 0 ? unchecked((int)0xFFFFFFFF) : unchecked((int)0xFF000000);
        }
      }

      BitmapData data = framebuffer.LockBits(rect, ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb);
      for (int y = 0; y < height; y++)
      {
        IntPtr rowPtr = IntPtr.Add(data.Scan0, y * data.Stride);
        Marshal.Copy(blitBuffer, y * width, rowPtr, width);
      }
      framebuffer.UnlockBits(data);
    }

    private void FlushFramebuffer()
    {
      Rectangle rect;
      lock (pixelLock)
      {
        if (!hasDirty)
        {
          if (cursorDirty)
          {
            cursorDirty = false;
            display.Invalidate();
          }
          return;
        }
        rect = new Rectangle(dirtyMinX, dirtyMinY, dirtyMaxX - dirtyMinX + 1, dirtyMaxY - dirtyMinY + 1);
        WriteDirtyRegionToBitmap(rect);
        hasDirty = false;
        cursorDirty = false;
      }
      display.Invalidate();
    }

    public void put_pixel(int x, int y, int color)
    {
      if (x < 0 || x >= FW || y < 0 || y >= FH) return;
      byte value = (byte)(color != 0 ? 1 : 0);
      int index = (y * FW) + x;
      lock (pixelLock)
      {
        if (pixelBuffer[index] == value) return;

        pixelBuffer[index] = value;


        MarkDirtyNoLock(x, y);


      }
    }

    public int get_pixel(int x, int y)
    {


      if (x < 0 || x >= FW || y < 0 || y >= FH) return 0;

      int index = (y * FW) + x;






      lock (pixelLock)
      {
        return pixelBuffer[index] != 0 ? 1 : 0;

      }
    }

    private void Display_Paint(object sender, PaintEventArgs e)
    {
      int imageX = (display.Width - framebuffer.Width) / 2;
      int imageY = (display.Height - framebuffer.Height) / 2;

      using (Pen border = new Pen(Color.DarkBlue, 4))
      {
        e.Graphics.DrawRectangle(border, imageX - 2, imageY - 2, framebuffer.Width + 3, framebuffer.Height + 3);
      }

      using (Pen pen = new Pen(Color.Red, 2))
      {
        int localTx, localTy;
        lock (cursorLock)
        {
          localTx = tx;
          localTy = ty;
        }
        int cursorX = imageX + localTx;
        int cursorY = imageY + localTy;
        e.Graphics.DrawRectangle(pen, cursorX, cursorY, FONT_W, FONT_H);
      }
    }

    public void clear_screen()
    {
      lock (pixelLock)
      {
        Array.Clear(pixelBuffer, 0, pixelBuffer.Length);
        MarkFullDirtyNoLock();
      }
    }

    const int FONT_W = 8;  // Font width
    const int FONT_H = 16; // Font height
    const int FW = 640;    // Screen width
    const int FH = 480;    // Screen height
    int tx = 0;            // Cursor x position
    int ty = 0;            // Cursor y position

    byte Reverse(byte b)
    {
      b = (byte)((b & 0xF0) >> 4 | (b & 0x0F) << 4);
      b = (byte)((b & 0xCC) >> 2 | (b & 0x33) << 2);
      b = (byte)((b & 0xAA) >> 1 | (b & 0x55) << 1);
      return b;
    }

    // Special handling result: 0 = none, 1 = handled (no clamp needed), 2 = handled and needs clamp/scroll check
    int HandleSpecialCharacter(int c)
    {
      if (c == 8 || c == 127) // BACKSPACE/DELETE
      {
        int eraseX, eraseY;
        lock (cursorLock)
        {
          if (tx - FONT_W < 0)
          {
            cursorDirty = true;
            return 1;
          }
          tx -= FONT_W;
          eraseX = tx;

          eraseY = ty;
          cursorDirty = true;
        }
        DrawGlyphAt(eraseX, eraseY, ' ');
        return 1;
      }
      else if (c == 9) // TAB
      {
        int ts;
        lock (cursorLock) { ts = tx / FONT_W; }
        for (int i = ts; i < ((ts + 7) / 8) * 8; i++)
        {
          Dputc(' ');
        }
        return 1;
      }
      else if (c == 10 || c == 13) // CR or LF
      {
        lock (cursorLock)
        {
          ty += FONT_H;
          tx = 0;
          cursorDirty = true;
        }
        return 2;
      }
      return 0;
    }

    void DrawGlyphAt(int x, int y, int c)
    {
      int index = c * ((FONT_H * FONT_W) / 8);
      byte[] bitmap = new byte[FONT_H];
      Array.Copy(font, index, bitmap, 0, FONT_H);
      HandleRegularCharacter(bitmap, x, y);
    }

    void HandleRegularCharacter(byte[] bitmap, int originX, int originY)
    {
      for (int x = 0; x < FONT_H; x++)
      {
        for (int y = 0; y < FONT_W; y++)
        {
          int set = Reverse(bitmap[x]) & (1 << y);
          put_pixel(originX + y, originY + x, set != 0 ? -1 : 0);
        }
      }

    }

    void AdvanceCursorAndScroll()
    {
      bool needScroll = false;
      lock (cursorLock)
      {
        if (tx >= FW)
        {
          tx = 0;
          ty += FONT_H;
        }
        if (ty > (FH - FONT_H))
        {
          ty = FH - FONT_H;
          needScroll = true;
        }
        cursorDirty = true;
      }
      if (needScroll) Scroll();
    }

    public void Scroll()
    {
      lock (pixelLock)
      {




        int rowWidth = FW;
        int srcOffset = FONT_H * rowWidth;
        int copyBytes = (FH - FONT_H) * rowWidth;
        Buffer.BlockCopy(pixelBuffer, srcOffset, pixelBuffer, 0, copyBytes);
        Array.Clear(pixelBuffer, copyBytes, FONT_H * rowWidth);



        MarkFullDirtyNoLock();
      }
    }

    void Redraw()
    {
      cursorDirty = true;
    }

    void Dputc(int c)
    {
      int special = HandleSpecialCharacter(c);
      if (special != 0)
      {
        if (special == 2) AdvanceCursorAndScroll();
        return;
      }

      int index = c * ((FONT_H * FONT_W) / 8);
      byte[] bitmap = new byte[FONT_H];

      Array.Copy(font, index, bitmap, 0, FONT_H);
      int originX, originY;
      lock (cursorLock)
      {
        originX = tx;
        originY = ty;
      }
      HandleRegularCharacter(bitmap, originX, originY);
      lock (cursorLock)
      {
        tx = originX + FONT_W;
        ty = originY;
        cursorDirty = true;
      }
      AdvanceCursorAndScroll();
    }

    private int[] acc = new int[32];
    private int acc_i = 0;
    private int on = 0;
262
263
264
265
266
267
268


269
270


271
272
273
274


275
276


277
278
279
280


281
282


283
284
285
286


287
288


289
290
291
292
293
294
295
296


297
298
299
300
301
302


303
304
305


306
307


308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326


327
328


329
330
331
332
333
334
335
    {
      for (int x = 0; x < 32; x++) acc[x] = 0;
      ground = on = 0;
    }

    public void gt_up(int n)
    {


      ty -= FONT_H;
      if (ty < 0) ty = 0;


    }

    public void gt_down(int n)
    {


      ty += FONT_H;
      if (ty > 24 * FONT_H) ty = 24 * FONT_H;


    }

    public void gt_left(int n)
    {


      tx -= FONT_W;
      if (tx < 0) tx = 0;


    }

    public void gt_right(int n)
    {


      tx += FONT_W;
      if (tx > 79 * FONT_W) tx = 79 * FONT_W;


    }

    public void gt_move_cursor()
    {
      if (acc_i >= 1)
      {
        int a = (acc_pop() - 1) * FONT_W;
        int b = (acc_pop() - 1) * FONT_H;


        ty = b;
        if (ty < 0) ty = 0;
        if (ty > FONT_H * 24) ty = FONT_H * 24;
        tx = a;
        if (tx < 0) tx = 0;
        if (tx > FONT_W * 79) tx = FONT_W * 79;


        acc_reset();
        return;
      }


      tx = 0;
      ty = 0;


      acc_reset();
    }

    public void gt_reset()
    {
      acc_reset();
    }

    public void gt_next()
    {
      acc_i += 1;
      if (acc_i >= acc.Length) acc_i = acc.Length - 1;
      acc[acc_i] = 0;
    }

    public void gt_clear()
    {
      acc_reset();
      clear_screen();


      tx = ty = 0;
      Redraw();


    }

    public void gt_set_attr()
    {
      acc_reset();
    }








>
>
|
|
>
>




>
>
|
|
>
>




>
>
|
|
>
>




>
>
|
|
>
>








>
>
|
|
|
|
|
|
>
>



>
>
|
|
>
>



















>
>
|
<
>
>







414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505

506
507
508
509
510
511
512
513
514
    {
      for (int x = 0; x < 32; x++) acc[x] = 0;
      ground = on = 0;
    }

    public void gt_up(int n)
    {
      lock (cursorLock)
      {
        ty -= FONT_H;
        if (ty < 0) ty = 0;
        cursorDirty = true;
      }
    }

    public void gt_down(int n)
    {
      lock (cursorLock)
      {
        ty += FONT_H;
        if (ty > 24 * FONT_H) ty = 24 * FONT_H;
        cursorDirty = true;
      }
    }

    public void gt_left(int n)
    {
      lock (cursorLock)
      {
        tx -= FONT_W;
        if (tx < 0) tx = 0;
        cursorDirty = true;
      }
    }

    public void gt_right(int n)
    {
      lock (cursorLock)
      {
        tx += FONT_W;
        if (tx > 79 * FONT_W) tx = 79 * FONT_W;
        cursorDirty = true;
      }
    }

    public void gt_move_cursor()
    {
      if (acc_i >= 1)
      {
        int a = (acc_pop() - 1) * FONT_W;
        int b = (acc_pop() - 1) * FONT_H;
        lock (cursorLock)
        {
          ty = b;
          if (ty < 0) ty = 0;
          if (ty > FONT_H * 24) ty = FONT_H * 24;
          tx = a;
          if (tx < 0) tx = 0;
          if (tx > FONT_W * 79) tx = FONT_W * 79;
          cursorDirty = true;
        }
        acc_reset();
        return;
      }
      lock (cursorLock)
      {
        tx = 0;
        ty = 0;
        cursorDirty = true;
      }
      acc_reset();
    }

    public void gt_reset()
    {
      acc_reset();
    }

    public void gt_next()
    {
      acc_i += 1;
      if (acc_i >= acc.Length) acc_i = acc.Length - 1;
      acc[acc_i] = 0;
    }

    public void gt_clear()
    {
      acc_reset();
      clear_screen();
      lock (cursorLock)
      {
        tx = ty = 0;

        cursorDirty = true;
      }
    }

    public void gt_set_attr()
    {
      acc_reset();
    }

508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
    public void ioen() { ip = 65536; }
    public void iost() { sp++; d[sp] = sp - 1; sp++; d[sp] = rp; }

    public void iopp() { int color = d[sp]; sp--;
      int y = d[sp]; sp--;
      int x = d[sp]; sp--;
      put_pixel(x, y, color);
      Redraw();
    }

    public void iogp() { int y = d[sp]; sp--; int x = d[sp]; d[sp] = get_pixel(x, y); }
    public void ioms() { Point pos = window.PointToClient(Cursor.Position);
      int x = pos.X;
      int y = pos.Y;
      bool leftDown = (Control.MouseButtons & MouseButtons.Left) != 0;







<







687
688
689
690
691
692
693

694
695
696
697
698
699
700
    public void ioen() { ip = 65536; }
    public void iost() { sp++; d[sp] = sp - 1; sp++; d[sp] = rp; }

    public void iopp() { int color = d[sp]; sp--;
      int y = d[sp]; sp--;
      int x = d[sp]; sp--;
      put_pixel(x, y, color);

    }

    public void iogp() { int y = d[sp]; sp--; int x = d[sp]; d[sp] = get_pixel(x, y); }
    public void ioms() { Point pos = window.PointToClient(Cursor.Position);
      int x = pos.X;
      int y = pos.Y;
      bool leftDown = (Control.MouseButtons & MouseButtons.Left) != 0;