-
Notifications
You must be signed in to change notification settings - Fork 8
/
Copy pathmos.c
3061 lines (2801 loc) · 95.1 KB
/
mos.c
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
400
401
402
403
404
405
406
407
408
409
410
411
412
413
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
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/*
** This file is part of the Matrix Brandy Basic VI Interpreter.
** Copyright (C) 2000-2014 David Daniels
** Copyright (C) 2014 Jonathan Harston
** Copyright (C) 2019 David Hawes (sound code, *-command parser)
** Copyright (C) 2018-2024 Michael McConnell, Jonathan Harston and contributors
**
** Brandy is free software; you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation; either version 2, or (at your option)
** any later version.
**
** Brandy is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with Brandy; see the file COPYING. If not, write to
** the Free Software Foundation, 59 Temple Place - Suite 330,
** Boston, MA 02111-1307, USA.
**
**
** This is one the files that contains functions that emulate
** some features of RISC OS such as the VDU drivers. The others
** are fileio.c, keyboard.c, textonly.c, graphsdl.c and
** riscos.c All OS-specific items should be put in these files.
**
** Some of the functions provided in here are not supported on
** any operating system other than RISC OS and in general using
** any of these in a program will result in an error. However,
** some of the features are cosmetic in that they do materially
** affect how the program runs, for example, the colours on the
** screen. There is a command line option, -ignore, that will
** allow the use of these features to be silently ignored rather
** than flagging them and bringing the program to a halt. The
** results might not look pretty but the program will run.
**
**
** Crispian Daniels August 20th 2002:
** Included Mac OS X target in conditional compilation.
**
** 06-Mar-2014 JGH: Rewritten *FX, simply calls OSBYTE, generalised
** decimal parser, added mos_osbyte(), added *HELP.
** *HELP reports main Brandy branch and fork.
** Rewritten *KEY with generalised decimal parser and
** generalise gstrans string converter.
** 29-Mar-2014 JGH: MinGW needs cursor restoring after system().
** 05-Apr-2014 JGH: Combined all the oscli() functions together.
** BEATS returns number of beats instead of current beat.
** 04-Dec-2018 JGH: *KEY checks if redefining the key currently being expanded.
** 05-Dec-2018 JGH: Added *SHOW, action currently commented out.
** 06-Dec-2018 JGH: *cd, *badcommand cursor position restored.
** 07-Dec-2018 JGH: *SHOW displays key string.
** Have removed some BODGE conditions.
** 09-Dec-2018 JGH: *HELP BASIC lists attributions, as per license.
** Layout and content needs a bit of tidying up.
** 05-Jan-2019 JGH: GSTrans returns string+length, so embedded |@ allowed in *KEY.
** 30-Aug-2019 JGH: Preparing to generalise OSBYTE 166+.
** 04-Sep-2019 JGH: Added OSBYTE system variables for OSBYTE 166+.
** 18-Oct-2019 JGH: Some keyboard OSBYTEs have to call keyboard module.
**
** Note to developers: after calling external command that generates screen output,
** need find_cursor() to restore VDU state and sometimes emulate_printf("\r\n") as well.
*/
#define _MOS_C
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <dirent.h>
//#include <stdarg.h>
#include <ctype.h>
#include <time.h>
#include "common.h"
#include "target.h"
#include "errors.h"
#include "basicdefs.h"
#include "mos.h"
#include "mos_sys.h"
#include "screen.h"
#include "keyboard.h"
#include "miscprocs.h"
#ifdef TARGET_RISCOS
#include "kernel.h"
#include "swis.h"
#endif
#ifdef TARGET_DJGPP
#include "dos.h"
#endif
#ifdef TARGET_MINGW
#include <windows.h>
#include <tchar.h>
//#include <strsafe.h>
#endif
#if defined(TARGET_MINGW) || defined(TARGET_UNIX) || defined(TARGET_MACOSX)
#include <sys/time.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/stat.h>
#endif
#ifdef USE_SDL
#include "SDL.h"
#include "graphsdl.h"
#include "soundsdl.h"
extern threadmsg tmsg;
#endif
#ifndef TARGET_RISCOS
static int check_command(char *text);
static void mos_osword(int32 areg, int64 xreg);
static uint32 mos_osbyte(int32 areg, int32 xreg, int32 yreg, int32 xflag);
static void native_oscli(char *command, char *respfile, FILE *respfh);
#endif
/* Address range used to identify emulated calls to the BBC Micro MOS */
#define LOW_MOS 0xFFC0
#define HIGH_MOS 0xFFF7
/* Emulated BBC MOS calls */
#define BBC_OSRDCH 0xFFE0
#define BBC_OSASCI 0xFFE3
#define BBC_OSNEWL 0xFFE7
#define BBC_OSWRCH 0xFFEE
#define BBC_OSWORD 0xFFF1
#define BBC_OSBYTE 0xFFF4
#define BBC_OSCLI 0xFFF7
/* Some defines for the Raspberry Pi GPIO support */
#define GPSET0 7
#define GPSET1 8
#define GPCLR0 10
#define GPCLR1 11
#define GPLEV0 13
#define GPLEV1 14
#define GPPUD 37
#define GPPUDCLK0 38
#define GPPUDCLK1 39
#define PI_BANK (inregs[0].i >> 5)
#define PI_BIT (1<<(inregs[0].i & 0x1F))
#define PI_INPUT 0
#define PI_OUTPUT 1
#define PI_ALT0 4
#define PI_ALT1 5
#define PI_ALT2 6
#define PI_ALT3 7
#define PI_ALT4 3
#define PI_ALT5 2
static time_t startime; /* Adjustment subtracted in 'TIME' */
#ifdef BRANDY_PATCHDATE
char mos_patchdate[]=__DATE__;
#endif
static void show_meminfo() {
emulate_printf("\r\nMemory allocation information:\r\n");
emulate_printf(" Workspace is at &" FMT_SZX ", size is &" FMT_SZX "\r\n PAGE = &" FMT_SZX ", HIMEM = &" FMT_SZX "\r\n",
basicvars.workspace, basicvars.worksize, basicvars.page, basicvars.himem);
emulate_printf(" stacktop = &" FMT_SZX ", stacklimit = &" FMT_SZX "\r\n", basicvars.stacktop.bytesp, basicvars.stacklimit.bytesp);
emulate_printf(" Internal recursion limit = %d, current = %d\r\n", basicvars.maxrecdepth, basicvars.recdepth);
#ifdef USE_SDL
emulate_printf(" Video frame buffer is at &" FMT_SZX ", size &%X\r\n", matrixflags.modescreen_ptr, matrixflags.modescreen_sz);
emulate_printf(" MODE 7 Teletext frame buffer is at &" FMT_SZX "\r\n", MODE7FB);
#endif /* USE_SDL */
if (matrixflags.gpio) emulate_printf(" GPIO interface mapped at &" FMT_SZX "\r\n", matrixflags.gpiomem);
}
static void cmd_brandyinfo() {
emulate_printf("\r\n%s\r\n", IDSTRING);
#ifdef BRANDY_GITCOMMIT
#ifdef BRANDY_RELEASE
emulate_printf(" Git commit %s on branch %s (%s)\r\n", BRANDY_GITCOMMIT, BRANDY_GITBRANCH, BRANDY_GITDATE);
#endif /* BRANDY_RELEASE */
#endif /* BRANDY_GITCOMMIT */
// Try to get attributions correct, as per license.
emulate_printf(" Forked from Brandy Basic v1.20.1 (24 Sep 2014)\r\n");
emulate_printf(" Merged Banana Brandy Basic v0.02 (05 Apr 2014)\r\n");
#ifdef BRANDY_PATCHDATE
emulate_printf(" Patch %s compiled at %s on ", BRANDY_PATCHDATE, __TIME__);
emulate_printf("%c%c %c%c%c %s\r\n", mos_patchdate[4]==' ' ? '0' : mos_patchdate[4],
mos_patchdate[5], mos_patchdate[0], mos_patchdate[1], mos_patchdate[2], &mos_patchdate[7]);
// NB: Adjust spaces in above to align version and date strings correctly
#endif
show_meminfo();
emulate_printf("Networking facilities ");
if (matrixflags.networking == 0) emulate_printf("not ");
emulate_printf("available\r\n");
emulate_printf("\r\n");
}
/* =================================================================== */
/* ======= Emulation functions common to all operating systems ======= */
/* =================================================================== */
/*
** 'emulate_mos' provides an emulation of some of the BBC Micro
** MOS calls emulated by the Acorn interpreter
** This is only 32-bit clean, it will not work reliably on 64-bit systems.
*/
static int32 emulate_mos(int32 address) {
int32 areg, xreg, yreg;
areg = basicvars.staticvars[A_PERCENT].varentry.varinteger;
xreg = basicvars.staticvars[X_PERCENT].varentry.varinteger;
yreg = basicvars.staticvars[Y_PERCENT].varentry.varinteger;
switch (address) {
case BBC_OSBYTE:
#ifdef TARGET_RISCOS
return ((_kernel_osbyte(areg, xreg, yreg) << 8) | areg);
#else
return mos_osbyte(areg, xreg, yreg, 0);
#endif
break;
case BBC_OSWORD:
#ifdef TARGET_RISCOS
(void) _kernel_osword(areg, (int *)xreg);
#else
if ((yreg & 0xFF000000) == 0xFF000000) {
/* Y register borked due to high bit. Let's try to shore it up. */
yreg = (yreg & 0xFFFFFF) -1;
}
if ((xreg & 0xFFFFFF00) == 0xFFFFFF00) {
/* MOD on a high-bit value sets ALL the bits. So, if MOD 256 used, let's strip them out. */
xreg &= 0xFF;
}
mos_osword(areg, xreg | ( yreg << 8));
#endif
return areg;
case BBC_OSWRCH: /* OSWRCH - Output a character */
emulate_vdu(areg);
return areg;
case BBC_OSRDCH:
return(kbd_get() & 0xFF);
break;
case BBC_OSASCI:
if (areg != 13) {
emulate_vdu(areg);
return(areg);
}
/* Deliberate fall-through to OSNEWL */
case BBC_OSNEWL:
emulate_printf("\r\n");
return areg;
case BBC_OSCLI:
mos_oscli((char *)(size_t)(xreg | ( yreg << 8)), NIL, NULL);
break;
}
return 0;
}
/*
** 'mos_call' deals with the Basic 'CALL' statement. This is
** unsupported except to provide limited support for the BBC MOS
** calls supported by the Acorn interpreter
*/
void mos_call(int32 address, int32 parmcount, int32 parameters []) {
if (parmcount==0 && address>=LOW_MOS && address<=HIGH_MOS)
emulate_mos(address);
else {
error(ERR_UNSUPPORTED);
}
}
/*
** 'mos_usr' deals with the Basic function 'USR'. It
** provides some limited support for the BBC MOS calls
** emulated by USR in the Acorn interpreter (where the
** called address is in the range 0xFFF4 to 0xFFC5)
*/
int32 mos_usr(int32 address) {
if (address>=LOW_MOS && address<=HIGH_MOS) return emulate_mos(address);
error(ERR_UNSUPPORTED);
return 0;
}
/* =================================================================== */
/* ================== RISC OS versions of functions ================== */
/* =================================================================== */
#ifdef TARGET_RISCOS
/* OS_Word and OS_Byte calls used */
#define WRITE_PALETTE 12 /* OS_Word call number to set palette entry */
#define CONTROL_MOUSE 21 /* OS_Word call number to control the mouse pointer */
#define SELECT_MOUSE 106 /* OS_Byte call number to select a mouse pointer */
/* Processor flag bits used in mos_sys() */
#define OVERFLOW_FLAG 1
#define CARRY_FLAG 2
#define ZERO_FLAG 4
#define NEGATIVE_FLAG 8
int64 mos_centiseconds(void) {
basicvars.centiseconds = clock();
return basicvars.centiseconds; // (clock() * 100) / CLOCKS_PER_SEC;
}
/*
** 'mos_rdtime' is called to deal with the Basic pseudo-variable
** 'TIME' to return its current value. Under RISC OS, the C function
** 'clock' returns the current value of the centisecond clock, which
** is just what we want
*/
int32 mos_rdtime(void) {
return clock()-startime;
}
/*
** 'mos_wrtime' handles assignments to the Basic pseudo variable 'TIME'.
** As adjusting 'TIME' is frowned upon the code emulates the effects
** of doing this
*/
void mos_wrtime(int32 time) {
startime = clock()-time;
}
/*
** 'mos_wrrtc' is called to handle assignments to the Basic
** pseudo variable 'TIME$'. This is used to set the computer's clock.
** It does not seem to be worth the effort of parsing the string, nor
** does it seem worth rejecting the assignment so this code just
** quietly ignores it
*/
void mos_wrrtc(char *time) {
}
/*
** 'mos_mouse_on' turns on the mouse pointer
*/
void mos_mouse_on(int32 pointer) {
(void) _kernel_osbyte(SELECT_MOUSE, 1, 0); /* R1 = 1 = select mouse pointer 1 */
}
/*
** 'mos_mouse_off' turns off the mouse pointer
*/
void mos_mouse_off(void) {
(void) _kernel_osbyte(SELECT_MOUSE, 0, 0); /* R1 = 0 = do not show the mouse pointer */
}
/*
** 'mos_mouse_to' moves the mouse pointer to position (x,y) on the screen
*/
void mos_mouse_to(int32 x, int32 y) {
struct {byte call_number, x_lsb, x_msb, y_lsb, y_msb;} osword_parms;
osword_parms.call_number = 3; /* OS_Word 21 call 3 sets the mouse position */
osword_parms.x_lsb = x & BYTEMASK;
osword_parms.x_msb = x>>BYTESHIFT;
osword_parms.y_lsb = y & BYTEMASK;
osword_parms.y_msb = y>>BYTESHIFT;
(void) _kernel_osword(CONTROL_MOUSE, TOINTADDR(&osword_parms));
}
/*
** 'mos_mouse_step' defines the mouse movement multipliers
*/
void mos_mouse_step(int32 xmult, int32 ymult) {
struct {byte call_number, xmult, ymult;} osword_parms;
osword_parms.call_number = 2; /* OS_Word 21 call 2 defines the mouse movement multipliers */
osword_parms.xmult = xmult;
osword_parms.ymult = ymult;
(void) _kernel_osword(CONTROL_MOUSE, TOINTADDR(&osword_parms));
}
/*
** 'mos_mouse_colour' sets one of the colours used for the mouse pointer
*/
void mos_mouse_colour(int32 colour, int32 red, int32 green, int32 blue) {
struct {byte ptrcol, mode, red, green, blue;} osword_parms;
osword_parms.ptrcol = colour;
osword_parms.mode = 25; /* Setting mouse pointer colour */
osword_parms.red = red;
osword_parms.green = green;
osword_parms.blue = blue;
(void) _kernel_osword(WRITE_PALETTE, TOINTADDR(&osword_parms));
}
/*
** 'mos_mouse_rectangle' defines the mouse bounding box
*/
void mos_mouse_rectangle(int32 left, int32 bottom, int32 right, int32 top) {
struct {
byte call_number,
left_lsb, left_msb,
bottom_lsb, bottom_msb,
right_lsb, right_msb,
top_lsb, top_msb;
} osword_parms;
osword_parms.call_number = 1; /* OS_Word 21 call 1 defines the mouse bounding box */
osword_parms.left_lsb = left & BYTEMASK; /* Why didn't Acorn provide a nice, clean SWI */
osword_parms.left_msb = left>>BYTESHIFT; /* for this call? */
osword_parms.bottom_lsb = bottom & BYTEMASK;
osword_parms.bottom_msb = bottom>>BYTESHIFT;
osword_parms.right_lsb = right & BYTEMASK;
osword_parms.right_msb = right>>BYTESHIFT;
osword_parms.top_lsb = top & BYTEMASK;
osword_parms.top_msb = top>>BYTESHIFT;
(void) _kernel_osword(CONTROL_MOUSE, TOINTADDR(&osword_parms));
}
/*
** 'mos_mouse' returns the current position of the mouse
*/
void mos_mouse(size_t values[]) {
_kernel_oserror *oserror;
_kernel_swi_regs regs;
oserror = _kernel_swi(OS_Mouse, ®s, ®s);
if (oserror!=NIL) {
error(ERR_CMDFAIL, oserror->errmess);
return;
}
values[0] = regs.r[0]; /* Mouse X coordinate is in R0 */
values[1] = regs.r[1]; /* Mouse Y coordinate is in R1 */
values[2] = regs.r[2]; /* Mouse button state is in R2 */
values[3] = regs.r[3]; /* Timestamp is in R3 */
}
/*
** 'mos_adval' implements the Basic function 'ADVAL' which returns
** either the number of free/waiting entries in buffer 'x' or the
** current value of an input device such as A/D convertor or mouse.
** If x<0 reads buffer status, if x>=0 reads input device.
*/
int32 mos_adval(int32 x) {
// _kernel_oserror *oserror;
_kernel_swi_regs regs;
regs.r[0] = 128; /* Use OS_Byte 128 for this */
regs.r[1] = x;
regs.r[2] = x >> 8;
// oserror = _kernel_swi(OS_Byte, ®s, ®s);
_kernel_swi(OS_Byte, ®s, ®s);
// Bug in RISC OS, *mustn't* return an error here
return regs.r[1]+(regs.r[2]<<BYTESHIFT);
}
/*
** 'mos_sound_on' handles the Basic 'SOUND ON' statement
*/
void mos_sound_on(void) {
_kernel_oserror *oserror;
_kernel_swi_regs regs;
regs.r[0] = 2; /* Turn on the sound system */
oserror = _kernel_swi(Sound_Enable, ®s, ®s);
if (oserror!=NIL) error(ERR_CMDFAIL, oserror->errmess);
}
/*
** 'mos_sound_off' handles the Basic 'SOUND OFF' statement
*/
void mos_sound_off(void) {
_kernel_oserror *oserror;
_kernel_swi_regs regs;
regs.r[0] = 1; /* Turn off the sound system */
oserror = _kernel_swi(Sound_Enable, ®s, ®s);
if (oserror!=NIL) error(ERR_CMDFAIL, oserror->errmess);
}
/*
** 'mos_sound' handles the Basic 'SOUND' statement.
** Note that this code ignores the 'delay' parameter
*/
void mos_sound(int32 channel, int32 amplitude, int32 pitch, int32 duration, int32 delay) {
_kernel_oserror *oserror;
_kernel_swi_regs regs;
regs.r[0] = channel;
regs.r[1] = amplitude;
regs.r[2] = pitch;
regs.r[3] = duration;
oserror = _kernel_swi(Sound_Enable, ®s, ®s);
if (oserror!=NIL) error(ERR_CMDFAIL, oserror->errmess);
}
/*
** 'mos_wrbeat' implments the Basic statement 'BEATS'
*/
void mos_wrbeat(int32 barlength) {
_kernel_oserror *oserror;
_kernel_swi_regs regs;
regs.r[0] = barlength;
oserror = _kernel_swi(Sound_QBeat, ®s, ®s);
if (oserror!=NIL) error(ERR_CMDFAIL, oserror->errmess);
}
/*
** 'mos_rdbeat' implements the Basic functions 'BEAT', which
** returns the current microbeat.
*/
int32 mos_rdbeat(void) {
_kernel_oserror *oserror;
_kernel_swi_regs regs;
regs.r[0] = 0;
oserror = _kernel_swi(Sound_QBeat, ®s, ®s);
if (oserror!=NIL) error(ERR_CMDFAIL, oserror->errmess);
return regs.r[0];
}
/*
** 'mos_rdbeats' implements the Basic functions 'BEATS', which
** returns the number of microbeats in a bar.
*/
int32 mos_rdbeats(void) {
_kernel_oserror *oserror;
_kernel_swi_regs regs;
regs.r[0] = -1;
oserror = _kernel_swi(Sound_QBeat, ®s, ®s);
if (oserror!=NIL) error(ERR_CMDFAIL, oserror->errmess);
return regs.r[0];
}
/*
** 'mos_wrtempo' implements the Basic statement 'TEMPO'
*/
void mos_wrtempo(int32 x) {
_kernel_oserror *oserror;
_kernel_swi_regs regs;
regs.r[0] = x;
oserror = _kernel_swi(Sound_QTempo, ®s, ®s);
if (oserror!=NIL) error(ERR_CMDFAIL, oserror->errmess);
}
/*
** 'mos_rdtempo' emulates the Basic function 'TEMPO'
*/
int32 mos_rdtempo(void) {
_kernel_oserror *oserror;
_kernel_swi_regs regs;
regs.r[0] = 0;
oserror = _kernel_swi(Sound_QTempo, ®s, ®s);
if (oserror!=NIL) error(ERR_CMDFAIL, oserror->errmess);
return regs.r[0];
}
/*
**'mos_voice' attaches the voice named to a voice channel
*/
void mos_voice(int32 channel, char *name) {
_kernel_oserror *oserror;
_kernel_swi_regs regs;
regs.r[0] = channel;
regs.r[1] = (int)(name);
oserror = _kernel_swi(Sound_AttachNamedVoice, ®s, ®s);
if (oserror!=NIL) error(ERR_CMDFAIL, oserror->errmess);
}
/*
** 'mos_voices' handles the Basic statement 'VOICES' which
** is define to set the number of voice channels to be used
*/
void mos_voices(int32 count) {
_kernel_oserror *oserror;
_kernel_swi_regs regs;
regs.r[0] = count; /* Number of voice channels to usse */
regs.r[1] = 0;
regs.r[2] = 0;
regs.r[3] = 0;
regs.r[4] = 0;
oserror = _kernel_swi(Sound_Configure, ®s, ®s);
if (oserror!=NIL) error(ERR_CMDFAIL, oserror->errmess);
}
void mos_stereo(int32 channel, int32 position) {
_kernel_oserror *oserror;
_kernel_swi_regs regs;
regs.r[0] = channel;
regs.r[1] = position;
oserror = _kernel_swi(Sound_Stereo, ®s, ®s);
if (oserror!=NIL) error(ERR_CMDFAIL, oserror->errmess);
}
/*
** 'read_monotonic' returns the current value of the monotonic
** timer
*/
static int32 read_monotonic(void) {
_kernel_oserror *oserror;
_kernel_swi_regs regs;
oserror = _kernel_swi(OS_ReadMonotonicTime, ®s, ®s);
if (oserror!=NIL) error(ERR_CMDFAIL, oserror->errmess);
return regs.r[0]; /* Value of timer is returned in R0 */
}
/*
** 'mos_waitdelay' emulate the Basic statement 'WAIT <time>'
*/
void mos_waitdelay(int32 delay) {
int32 target;
_kernel_swi_regs regs;
if (delay<=0) return; /* Nothing to do */
target = read_monotonic()+delay;
/*
** Wait for 'delay' centiseconds. OS_Byte 129 is again misused
** here to make the program pause
*/
do {
_kernel_oserror *oserror;
if (delay>32767) delay = 32767; /* Maximum time OS_Byte 129 can wait */
regs.r[0] = 129;
regs.r[1] = delay & BYTEMASK;
regs.r[2] = delay>>BYTESHIFT;
oserror = _kernel_swi(OS_Byte, ®s, ®s);
if (oserror!=NIL) {
error(ERR_CMDFAIL, oserror->errmess);
return;
}
if (regs.r[2]==ESC || basicvars.escape) break; /* Escape was pressed - Escape from wait */
delay = target-read_monotonic();
} while (delay>0);
}
/*
** 'mos_setend' emulates the 'END=' form of the 'END' statement. This
** can be used to extend the Basic workspace to. It is not supported by
** this version of the interpreter
*/
void mos_setend(int32 newend) {
error(ERR_UNSUPPORTED);
}
/*
** This is the RISC OS-specific version of 'oscli' that uses the low
** level library functions to issue the command passed to it in order
** to better trap any errors. 'respfile' is set to NIL if the command
** output is to displayed in the normal way. If it is not NIL, the
** command output is redirected to it.
** Note that it is assumed that the buffer holding the command
** string is large enough to have the redirection commands
** appended to it. (basicvars.stringwork is used for this and so
** it should not be a problem unless the size of stringwork is
** drastically reduced.)
*/
void mos_oscli(char *command, char *respfile, FILE *respfh) {
if (respfile==NIL) { /* Command output goes to normal place */
if (!strncasecmp(command, "brandyinfo", 10)) {
cmd_brandyinfo();
} else if ( (!strncasecmp(command, "refresh", 7)) ||
(!strncasecmp(command, "fullscreen", 10)) ||
(!strncasecmp(command, "wintitle", 8)) ) {
/* Do nothing - Matrix Brandy specific commands for other platforms */
} else {
basicvars.retcode = _kernel_oscli(command);
if (basicvars.retcode<0) {
error(ERR_CMDFAIL, _kernel_last_oserror()->errmess);
return;
}
}
}
else { /* Want response back from command */
STRLCAT(command, "{ > ", MAXSTRING);
STRLCAT(command, respfile, MAXSTRING);
STRLCAT(command, " }", MAXSTRING);
basicvars.retcode = _kernel_oscli(command);
if (basicvars.retcode<0) { /* Call failed */
remove(respfile);
error(ERR_CMDFAIL, _kernel_last_oserror()->errmess);
}
}
}
/* This gets the virtual SWI num of Brandy-specific calls that are only valid within Matrix Brandy */
static int32 mos_getswinum2(char *name, int32 length, int32 inxflag) {
int32 ptr;
int32 xflag=0;
char namebuffer[128];
if (name[0] == 'X') {
name++;
length--;
xflag=0x20000;
}
for (ptr=0; swilist[ptr].swinum!=0xFFFFFFFF; ptr++) {
if ((!strncmp(name, swilist[ptr].swiname, length)) && length==strlen(swilist[ptr].swiname)) break;
}
strncpy(namebuffer,name, length);
namebuffer[length]='\0';
if (swilist[ptr].swinum==0xFFFFFFFF) {
if (inxflag != XBIT) error(ERR_SWINAMENOTKNOWN, namebuffer);
return (-1);
} else {
return ((swilist[ptr].swinum)+xflag);
}
}
/*
** 'mos_getswinum' returns the SWI number corresponding to
** SWI 'name'
*/
size_t mos_getswinum(char *name, int32 length, int32 inxflag) {
int32 ilength = length;
char *iname = name;
_kernel_oserror *oserror;
_kernel_swi_regs regs;
char swiname[100];
if (length==0) length = strlen(name);
memmove(swiname, name, length);
swiname[length] = NUL; /* Ensure name is null-terminated */
regs.r[1] = (int)(&swiname[0]);
oserror = _kernel_swi(OS_SWINumberFromString, ®s, ®s);
if (oserror!=NIL) return mos_getswinum2(iname, ilength, inxflag);
return regs.r[0];
}
/*
** 'mos_sys' issues a SWI call and returns the result.
** The code fakes the processor flags returned in 'flags'. The carry
** flag is returned by the call to _kernel_swi_c(). The state of the
** overflow bit can be determined by checking if the swi function
** returns null. The Acorn interpreter always seems to set the zero
** flag and the sign flag can probably be ignored.
** The layout of the flags bits are:
** N (sign) Z (zero) C (carry) V (overflow)
*/
void mos_sys(size_t swino, sysparm inregs[], size_t outregs[], size_t *flags) {
_kernel_oserror *oserror;
_kernel_swi_regs regs;
int n;
if ((swino & ~XBIT) == 57) { /* OS_SWINumberFromString - call our local version */
outregs[1]=inregs[1].i;
for(n=0;*(char *)(inregs[1].i+n) >=32; n++) ;
*(char *)(inregs[1].i+n)='\0';
outregs[0]=mos_getswinum((char *)inregs[1].i, strlen((char *)inregs[1].i), (swino & XBIT));
} else if (swino >= 0x140000) {
/* Brandy-specific virtual SYS calls */
mos_sys_ext(swino & ~XBIT, inregs, outregs, swino & XBIT, flags);
} else {
for (n=0; n<10; n++) regs.r[n] = inregs[n].i;
oserror = _kernel_swi_c(swino, ®s, ®s, (int *)flags);
if (oserror!=NIL && (swino & XBIT)==0) {
error(ERR_CMDFAIL, oserror->errmess);
return;
}
*flags = *flags!=0 ? CARRY_FLAG : 0;
if (oserror!=NIL) *flags+=OVERFLOW_FLAG;
for (n=0; n<10; n++) outregs[n] = regs.r[n];
}
}
/*
** mos_init - Called to initialise the emulation code
*/
boolean mos_init(void) {
startime = 0;
return TRUE;
}
void mos_final(void) {
}
#else /* not TARGET_RISCOS */
/* ====================================================================== *
* ================== Non-RISC OS versions of functions ================= *
* ====================================================================== */
#if (defined(TARGET_WIN32) | defined(TARGET_AMIGA)) && !defined(TARGET_MINGW)
int64 mos_centiseconds(void) {
basicvars.centiseconds = (clock() * 100) / CLOCKS_PER_SEC
return basicvars.centiseconds;
}
/*
** 'mos_rdtime' is called to deal with the Basic pseudo-variable
** 'TIME' to return its current value. This should be the current
** value of the centisecond clock, but how accurate the value is
** depends on the underlying OS.
*/
int32 mos_rdtime(void) {
return (clock() - startime) * 100 / CLOCKS_PER_SEC;
}
/*
** 'mos_wrtime' handles assignments to the Basic pseudo variable 'TIME'.
** The effects of 'TIME=' are emulated here
*/
void mos_wrtime(int32 time) {
startime = clock() -(time * CLOCKS_PER_SEC / 100);
}
#else
/*
** 'mos_rdtime' is called to deal with the Basic pseudo-variable
** 'TIME' to return its current value. This should be the current
** value of the centisecond clock, but how accurate the value is
** depends on the underlying OS.
** This code was supplied by Jeff Doggett, and modified
** by Michael McConnell
** Further modified by moving code into brandy.c and running in a
** separate thread that updates basicvars.centiseconds.
*/
int64 mos_centiseconds(void) {
return basicvars.centiseconds;
}
int32 mos_rdtime(void) {
return ((int32) (basicvars.centiseconds - startime));
}
/*
** 'mos_wrtime' handles assignments to the Basic pseudo variable 'TIME'.
** The effects of 'TIME=' are emulated here
*/
void mos_wrtime (int32 time) {
startime = (basicvars.centiseconds - time);
}
#endif
/* OSWORD call to read the centisecond clock */
static void osword01(int64 x) {
int32 *block;
block=(int32 *)(size_t)x;
*block=mos_rdtime();
}
/* OSWORD call to write the centisecond clock */
static void osword02(int64 x) {
int32 *block;
block=(int32 *)(size_t)x;
mos_wrtime(*block);
}
/*
** 'mos_wrrtc' is called to handle assignments to the Basic
** pseudo variable 'TIME$'. This is used to set the computer's clock.
** It does not seem to be worth the effort of parsing the string, nor
** does it seem worth rejecting the assignment so this code just
** quietly ignores it
*/
void mos_wrrtc(char *time) {
}
/*
** 'mos_mouse_on' turns on the mouse pointer
*/
void mos_mouse_on(int32 pointer) {
#ifdef USE_SDL
sdl_mouse_onoff(1);
return;
#else
if (basicvars.runflags.flag_cosmetic) error(ERR_UNSUPPORTED);
#endif
}
/*
** 'mos_mouse_off' turns off the mouse pointer
*/
void mos_mouse_off(void) {
#ifdef USE_SDL
sdl_mouse_onoff(0);
return;
#else
if (basicvars.runflags.flag_cosmetic) error(ERR_UNSUPPORTED);
#endif
}
/*
** 'mos_mouse_to' moves the mouse pointer to (x,y) on the
** screen
*/
void mos_mouse_to(int32 x, int32 y) {
#ifdef USE_SDL
warp_sdlmouse(x,y);
return;
#else
if (basicvars.runflags.flag_cosmetic) error(ERR_UNSUPPORTED);
#endif
}
/*
** 'mos_mouse_step' changes the number of graphics units moved
** per step of the mouse to 'x' and 'y'.
*/
void mos_mouse_step(int32 x, int32 y) {
#ifdef USE_SDL
return; // Do nothing, silently.
#else
if (basicvars.runflags.flag_cosmetic) error(ERR_UNSUPPORTED);
#endif
}
/*
** 'mos_mouse_colour' sets colour 'colour' of the mouse sprite
** to the specified values 'red', 'green' and 'blue'
*/
void mos_mouse_colour(int32 colour, int32 red, int32 green, int32 blue) {
#ifdef USE_SDL
return; // Do nothing, silently.
#else
if (basicvars.runflags.flag_cosmetic) error(ERR_UNSUPPORTED);
#endif
}
/*
** 'mos_mouse_rectangle' restricts the mouse pointer to move
** in the rectangle defined by (left, bottom) and (right, top).
*/
void mos_mouse_rectangle(int32 left, int32 bottom, int32 right, int32 top) {
#ifdef USE_SDL
return; // Do nothing, silently.
#else
if (basicvars.runflags.flag_cosmetic) error(ERR_UNSUPPORTED);
#endif
}
/*
** 'mos_mouse' emulates the Basic 'MOUSE' statement
*/
void mos_mouse(size_t values[]) {
#ifdef USE_SDL
get_sdl_mouse(values);
#else
if (basicvars.runflags.flag_cosmetic) error(ERR_UNSUPPORTED);
#endif
}
/*
** 'mos_adval' emulates the Basic function 'ADVAL' which
** either returns the number of entries free in buffer 'x' or
** the current value of a device, eg A/D convertor, mouse, etc.
**
** Positive parameter - read device
** Negative parameter - read buffer
** 128-buffer - low-level raw read of buffer (extension)
**
** 0 b0-b7=buttons, b8-b15=last converted channel
** 1 ADC 1 - Device 1 X position
** 2 ADC 2 - Device 1 Y position
** 3 ADC 3 - Device 2 X position
** 4 ADC 4 - Device 2 Y position
** 5 Mouse X boundary
** 6 Mouse Y boundary
** 7 Mouse X position
** 8 Mouse Y position
** 9 Mouse button state %xxxxRML
** 10+ other devices
**
** -1 Keyboard buffer 127 Low-level examine keyboard buffer
** -2 Serial input buffer 126
** -3 Serial output buffer 125
** -4 Printer output buffer 124
** -5 Sound output buffer 0 123
** -6 Sound output buffer 1 122
** -7 Sound output buffer 2 121
** -8 Sound output buffer 3 120
** -9 Speach output buffer 119
** -10 Mouse input buffer 118
** -11 MIDI input buffer 117
** -12 MIDI output buffer 116
** -12- other buffers etc
*/
int32 mos_adval(int32 x) {
x = x & 0xFFFF; /* arg is a 16-bit value */
if((x>6) & (x<10)) {
size_t inputvalues[4]={0,0,0,0}; /* Initialise to zero to keep non-SDL builds happy */
mos_mouse(inputvalues);
return inputvalues[x-7];
}
if (x == 0x007F) return kbd_get0(); /* Low-level examine of keyboard buffer */
if (x == 0xFFFF) return kbd_buffered(); /* ADVAL(-1), amount in keyboard buffer */
return 0;
}
/*
** 'mos_sound_on' handles the Basic 'SOUND ON' statement
*/
void mos_sound_on(void) {
#ifdef USE_SDL
sdl_sound_onoff(1);
#else
if (basicvars.runflags.flag_cosmetic) error(ERR_UNSUPPORTED);
#endif
}
/*