|
Introduction
This
document outlines issues and notes involved in porting the
assembler and the emulator to different
platforms.
Porting the
assembler
6502asm should not present any
special challenges. It incorporates selectively-compiled code
for the DOS environment where you cannot create an array of
64K without using _far calls. On
other platforms (which would include Win32 as a console
application), it just assumes an array of this size is okay.
Output is to file and/or screen. Nothing special, so no 'odd'
libraries required.
Porting the emulator
AmélieEm, on the other hand,
makes extensive use of the functions provided by
conio and also some functions provided by
dos, not to mention the somewhat grotty idea of
directly peeking in memory to see if a key has been
pressed (because kbhit() is
amazingly slow!). You'll need to provide your own
implementations of these things. The basic rule
is that only the minimum amount of system-specific code
will be put into AmélieEm (i.e. filing system differences,
specific pragmas, etc). For the user interface, a "dos.h" and
"conio.h" should be written that fakes the Borland TurboC
versions.
Here is an example of
AmélieEm (v0.01af) running under RISC OS:

Here is the dos library
emulation I cobbled together for RISC OS. Note that it
does the translations, so the
existing emulator code does not need to be modified
for a RISC OS build. It is called dos.c
and it requires a header file to define these
functions globally.
/* Hacky implementation of required DOS.h
functions */
#include
"kernel.h" #include "dos.h"
void delay(int
d) {
_kernel_swi_regs r; int tickthen,
ticknow;
d = d / 10; /* milli -> centi */
_kernel_swi(0x20042, &r,
&r); //
XOS_ReadMonotonicTime tickthen =
r.r[0]; tickthen + =
d;
do
{
_kernel_swi(0x20042, &r, &r); //
XOS_ReadMonotonicTime ticknow =
r.r[0]; } while (ticknow <
tickthen);
return; }
int peek(int seg, int
addr) { /* This is used to peek in DOS memory for
the length of the keyboard
queue; as it is MUCH
faster than calling kbhit()...
The
command used (in the
emulator) is: if (
peek(0x0040, 0x001A)
!=
peek(0x0040, 0x001C) ) [then ...]
*/ _kernel_swi_regs
r; int reply;
if ((seg == 0x0040)
&& (addr ==
0x001A))
{ /* Return SIZE of keyboard buffer -
HARDCODED! */ return
255; }
if ((seg == 0x0040)
&& (addr ==
0x001C))
{ /* Return FREE SPACE in keyboard buffer
*/ r.r[0] =
128; r.r[1] =
255;
_kernel_swi(0x20006, &r, &r);
//
XOS_Byte
reply = 255 -
r.r[1];
return reply; }
/* Anything else? Ignore!
*/ return
0; }
What may perhaps need a
little bit of an explanation is the 0x0040:001A / 0x0040:001C stuff. You see, the
kbhit() call is very slow. What is
much faster is to look at the keyboard queue markers to
examine the pointers to the start and end of the queue. If
they are the same - voila! - no key has been pressed. Since
the addresses given are BIOS addresses, we need to fake
the peek() call, and fake also the
response to interrogating the keyboard status by returning
something relevant. Under RISC OS we cannot easily return
the address of the keyboard buffer markers, so we return the
free space and the byte remaining - both of
which will be "255" if there has been no key pressed. This
would be a place to patch in support for other hardware
(COM1:, LPT1:, etc) however please be aware that direct memory
access is somewhat unfriendly - in fact I wouldn't be
surprised if this makes XP wet itself. I don't have XP so
I can't tell
you...
Why is
kbhit() slow? Like any useful
operating system, DOS supports the concept of streams -
"keyboard" input could be coming from a file. We can
bypass that as it is rather unlikely anybody would use
AmélieEm
in such a manner, and in doing so we can
greatly speed things
up.
Some other PC
BIOS addresses which may prove
to be
useful:
| 0040:0000 |
Address of COM1 |
| 0040:0008 |
Address of LPT1 |
| 0040:0011 |
Equipment list (number of COM/LPT
ports). |
| 0040:001E |
32 byte circular keyboard
buffer |
| 0040:0097 |
Keyboard LED flags. |
For RISC
OS, I would advocate using the documented OS
interfaces as the system is cleaner and has a lower latency
than DOS/BIOS on a PC. The only exception is a possible speed
enhancement within the keyboard checking, because the OS
software interrupt OS_Byte 128, 255 actually does the following
(in RISC OS
2):
Osbyte80
AND R1, R1, #&FF
TST R1,
#&80
; is it
ADVAL(-n)
BEQ
AdvalPositive ; no, then
do adval(+ve)
EOR R1, R1,
#&FF ; convert
to buffer
number
CLC
; (C:=0 and
V:=0)
TEQ R1,
#Buff_Mouse ; is it mouse (only
input buf >= 2)
?
CMPNE R1, #Buff_RS423Out ; C=1 <=>
output buffer, so count
spaces
; V=0, so will do count not purge
ADR R14,
MyOsbyte80 CnpEntry
Push "R10,R12,R14"
MOV R10,
#CNPV
B GoVec
Since GoVec calls
CallAVector, it may make sense to try
calling the CnpV vector directly
via OS_CallAVector. This is
invariably something best handled in
assembler. Important! The
CallAVector routine in the OS takes the vector number
in R10 (as shown in the code above), however the
OS_CallAVector SWI takes the vector number in R9.
This is correct, the SWI entry point pushes the
vector into R10, sets up some stuff, calls
CallAVector, then tidies up after... The code
is:
;************************************************ ;
SWI to call a
vector ;************************************************ CallAVector_SWI ;
R9 is the vector number
(!!)
Push "lr"
MOV R10, R9
ORR R14, R14,
#SVC_mode
TEQP PC,
R14
; restore caller CCs
BL CallVector
MOV R10, PC, LSR
#28 ; restore
CCs
Pull "lr"
BIC lr, lr,
#&F0000000
ORR lr, lr, R10, LSL
#28
ExitSWIHandler
I'll leave it up to you to
decide if it is better calling vector or using the
OS_Byte call. For now, I'll do it
the OS_Byte way as it is easier.
Things may have changed in later versions of RISC OS due to
separation of mouse and keyboard drivers from the kernel, and
maybe more with HAL implementations, such that the
"documented" method is the only sane approach.
Next is the header for
the conio emulation. This
is conio.h...
/*
Hacky implementation of required CONIO.h functions
*/
#define
BLACK
0 #define
LIGHTRED
1 #define LIGHTGREEN
2 #define
YELLOW
3 #define LIGHTBLUE
4 #define LIGHTMAGENTA 5 #define
LIGHTCYAN 6 #define
WHITE
7
#define
RED
9 #define
GREEN
10 #define
BROWN
11 #define
BLUE
12 #define
MAGENTA
13 #define
CYAN
14 #define LIGHTGRAY
15 #define
DARKGRAY
8
#define
_NOCURSOR 0 #define
_NORMALCURSOR 1
typedef struct
text_info { unsigned
char winleft; unsigned
char wintop; unsigned
char winright; unsigned
char winbottom; unsigned
char attribute; unsigned
char normattr; unsigned
char currmode; unsigned
char screenheight; unsigned
char screenwidth; int
curx; int cury; };
extern void gotoxy(int, int); extern
void textcolor(int); extern void
textbackground(int); extern void cprintf(char *, ...);
extern void putch(int); extern void clrscr(void); extern
int wherex(void); extern int
wherey(void); extern void
clreol(void);
extern int getch(void); extern int
kbhit(void);
extern void
_setcursortype(int); extern void sound(int); extern void
nosound(void);
/* RISC OS's VDU
output is a singular entity... */ #define cputs
cprintf
extern void
gettextinfo(struct text_info *); extern void window(int,
int, int, int); extern void gettext(int, int, int, int,
char *); extern void puttext(int, int, int, int, char
*); extern int textattr(int); extern void
highvideo(void); extern void
lowvideo(void); extern void
normvideo(void);
What
may require explanation here is the cprintf() function. Under DOS, if you set up a
colour, you should use cprintf() to print text in
that colour, normal printf() prints in light grey as
normal. Under RISC OS, all screen output is performed
graphically so changing colour affects subsequent output,
be it writing text or drawing lines. The only thing that isn't
affected is the outline font system, but we make no use of
this.
Because the screen output is graphical, it is
not really possible to read a text character and 'attribute'
from screen the way DOS does it. Therefore the conio
emulation creates a copy of the screen information in memory,
which is then used alongside actual screen output.
Here is the lovely
amazingly hacky implementation of
conio.c... Note that it has one
important dependency - when the emulator starts, it assumes
that you are in the correct screenmode, with the correct
colourset, and the correct display font. The !Run
file loads the font and sets the screenmode. A little BASIC
program run before the main executable sets the colours. Note
also that colours are set using VDU calls. If we build a
WimpPalette and set that, it'll affect all subsequent
operation - including the Desktop. It is a pain in the ass on
a RiscPC because the 'enhanced' palette manager no longer
includes a way to restore the 'default' palette. Note that
there is practically no error checking. We 'assume' valid
values will always be given, as the code 'works' with the DOS
(Borland) version of the library.
Important - the
Amélie source includes a number of #ifdef
sections to cater for differences between DOS and RISC OS (and
the compilers used), however providing the same level of
support for all screen output would
turn the code into a total mess. Therefore it has been decided
(an "executive decision"!) that all
screen and keyboard stuff within Amélie will be via
conio, and any ports of Amélie will have to
figure out a way to emulate, fake, or otherwise rip-off how
conio
works.
There are annotations
within this source...
/* Name :
AmélieEm Version: v0.01af
Date : Saturday, 4th August 2007
Module : conio [ > >
RISC OS only < < ] Purpose: Hacky
implementation of CONIO and CGA text display for RISC
OS.
Creator: Rick Murray <heyrick
-at- merseymail -dot-
com> */
#include "stdio.h" #include
"kernel.h" #include "conio.h" #include
<stdarg.h> #include <stdlib.h> #include
<string.h> #include
"amem.h"
static int lastforecolour =
WHITE; static int lastbackcolour =
BLACK;
The use of these colours is mostly
historical. The first version of this library changed screen
mode for every clrscr, but this
caused unacceptable flicker. The second version set the
screen mode on the first call. Now that we support colour
and use the PC-ANSI-like display font, we simply clear the
screen and leave setting the mode/colours/font up to an
external program. The clrscr()
command uses the colours, so
don't get rid of them just yet... I
chose to use an external program as this provides the user
with greater flexibility to tweak the colours and/or mode (a
listing is given at the end). The colours chosen are the
values extracted from a saved screen from the Aleph1
!PC (PC emulator) software, which should roughly
correspond to those values which could
be extracted from a CGA display driver.
static int
ctrlgetch = 0; static
_kernel_swi_regs r; static unsigned char scrtext[ (80 *
25) ]; static unsigned char scrattr[ (80 * 25)
]; static int cursorx = 0; static int
cursory = 0; static int currattr = 0; static
unsigned char vdudata[16];
extern int
single_stepped; /* it is part of Tracey
*/
#define textwrite(x, y,
z) ( scrtext[((y)
* 80) + (x)] = (z) ) #define
textread(x,
y) ( scrtext[((y) * 80) + (x)]
) #define attrwrite(x, y, z) (
scrattr[((y) * 80) + (x)] =
(z) ) #define attrread(x,
y) ( scrattr[((y) * 80) +
(x)] )
#define
XOS_NewLine
0x20003 #define
XOS_ReadC
0x20004 #define
XOS_Byte
0x20006 #define
XOS_Exit
0x20011 #define
XOS_EnterOS
0x20016 #define
XOS_Module
0x2001E #define
XOS_Claim
0x2001F #define
XOS_ReadEscapeState
0x2002C #define
XOS_RemoveCursors
0x20036 #define
XOS_RestoreCursors
0x20037 #define
XOS_CallEvery
0x2003C #define
XOS_ChangeEnvironment
0x20040 #define
XOS_ReadMonotonicTime
0x20042 #define
XOS_WriteN
0x20046 #define
XIIC_Control
0x20240 #define
XWimp_CommandWindow
0x600EF #define
XWimp_ReadSysInfo
0x600F2 #define
XSound_Control
0x60189 #define XParallel_HardwareAddress
0x62EC0
Most of these aren't
used right now, only NewLine, ReadC, Byte,
RemoveCursors, and RestoreCursors. In any case, we can now call SWIs by
name.
void gotoxy(int x, int
y) { /*
Correct for CONIO starting at 1,1 */
x--; y--;
/* Set our copy position
*/ cursorx = x;
cursory = y;
/* Now
set the actual output position */
_kernel_oswrch(31);
_kernel_oswrch(x);
_kernel_oswrch(y);
return; }
Slight differences in technique - a
display starts at 1,1 under
DOS, or 0,0 under RISC
OS! Therefore the
x--,y--. We could have done
something like __kernel_oswrch(--x); cursorx = x; etc, the
code in either case should be more or less the same, just in a
different order.
void absgotoxy(int x, int
y) { /*
Absolute go-to-x,y : assumes position given is relative
to 0,0 */
/*
Set our copy position */ cursorx =
x; cursory = y;
/* Now set the actual output position
*/
_kernel_oswrch(31);
_kernel_oswrch(x);
_kernel_oswrch(y);
return; }
void
textcolor(int c) {
int currattr = 0;
/* Set our copy attribute
*/ currattr =
attrread(cursorx, cursory); currattr &=
0xF0;
/* mask to keep background
*/ currattr |= (c &
15); /* push in new
foreground */
attrwrite(cursorx, cursory,
currattr); /* THEORY: We
*could* alter the colour of any given output simply by
altering
the
attributes, and NOT reprinting the output afterwards.
Thankfully
AmélieEm does not do this, so we don't need to try to
support it! :-) */
/*
Now set the actual output colour */
_kernel_oswrch(17);
_kernel_oswrch(c);
lastforecolour = c;
return; }
void
textbackground(int c) {
int currattr = 0; int ccopy =
c;
/* Background
colours can ONLY be 'dim', unless black...
*/ if (ccopy >
0) {
if (ccopy <
8) ccopy +=
8; }
/* Set our copy attribute [and see above
comment] */ currattr =
attrread(cursorx, cursory);
currattr &=
0x0F;
/* mask to keep foreground
*/ currattr |= ((ccopy & 15)
<< 4); /* push in new
background */
attrwrite(cursorx, cursory,
currattr); currattr =
currattr;
/* Now set
the actual output colour */
_kernel_oswrch(17);
_kernel_oswrch(ccopy +
128); lastbackcolour =
ccopy;
return; }
As you
can now see, ASCII codes under 32 'do stuff' to the display.
So to change colour, for example, we write character 17,
followed by the colour number. There are other ways to
change colour, I think writing codes to the display is
probably the quickest - though I'll have to look at the VDU
driver source sometime to check it doesn't do something
dopey like figure out the colour to set and then call the
generic SWI... :-) Later: The
VDU driver code is a well-structured nightmare. I think I'll
pass on following where that's going!
void
cprintf(char *message,
...)
{ /* Accepts printf() style parameters
*/ int items;
char *out; va_list
variables; int recurse;
if ((out = (char
*)malloc(1024)) == NULL) /* Allocate a block of memory
*/
return;
va_start(variables,
message); items =
vsprintf(out, message,
variables);
for
(recurse = 0; recurse < strlen(out);
recurse++)
{ /*
omit linefeeds - much uses CRLF for DOSisms, RISC OS works
otherwise */
if ( out[recurse] != 13
)
{
if ( out[recurse] < 32
)
{
/* filter out control codes [shouldn't
happen!]
*/
if ( out[recurse] == 10
)
putch(10); // for
screen output
tracking
}
else
{
putch( out[recurse] ); // for screen output
tracking
} }
}
va_end(variables);
free(out);
return; }
We implement our own
cprintf() so that we can sanely
cater for
the CRLF line termination used on DOS systems (and within the
rest of AmélieEm's source), plus also allowing us to filter out
unwanted control codes. Note that we allocate a buffer
of 1024 bytes. If the expanded output is larger, Bad
Things Will Happen. I will see
if VarArgs can return a length in a later version of the
library. Important: For
all output where we are not managing
the cursor ourselves, we must call
putch().
void putch(int
b) { /*
We mingle phantom-screen and real-screen positioning
*/
if (b ==
10) {
/* New line
*/
cursory++; cursorx =
0;
/* Scrolling is NOT supported!
*/
if (cursory >
24)
cursory = 0; /* wrap to top instead
*/
/* Handle actual output positioning here
*/
absgotoxy(cursorx,
cursory); }
else
{ /*
Place cursor */
absgotoxy(cursorx,
cursory);
/* Output byte
*/
_kernel_oswrch(b);
/* And paste it into the screen copy,
along with attribute
*/ currattr =
((lastbackcolour << 4) |
lastforecolour);
textwrite(cursorx, cursory,
b);
attrwrite(cursorx, cursory,
currattr);
/* Now faff with the cursor
*/
cursorx++;
if (cursorx >
79)
{
cursorx =
0;
cursory++;
if (cursory >
24)
cursory = 0;
} }
return; }
Be aware that
putch() does not filter unwanted
control codes - watch passing anything other than ASCII
<10>.
The
cputs() code used to be
here. This
has been removed, with cputs() aliased to actually
call cprintf().
void
clrscr(void)
{ int lp
= 0;
/* Clear the
ACTUAL screen */
textcolor(lastforecolour);
textbackground(lastbackcolour);
_kernel_oswrch(12); /* CLS
*/
/* Clear the
array - ourselves, for speed */
currattr = ((lastbackcolour << 4) +
lastforecolour); for (lp =
0; lp < ((25 * 80) - 1); lp++)
{ scrtext[lp] = 32; /* all your bytes are spaces / all your base
are mine :-) */
scrattr[lp] = currattr;
}
cursorx = 0; cursory =
0;
return; }
int
wherex(void) { /* Read from our copy position
*/
// int r0, r1,
r2; // r0 = 134; r1 = 0; r2 =
0; // _kernel_osbyte(r0, r1,
r2); // return (r2 +
1);
return
(cursorx + 1); // +1 correction for
CONIO 1,1 start
offset }
int
wherey(void) { /* Read from our copy position
*/
// int r0, r1,
r2; // r0 = 134; r1 = 0; r2 =
0; // _kernel_osbyte(r0, r1,
r2); // return (r2 +
1);
return
(cursory + 1); // +1 correction for
CONIO 1,1 start
offset }
void
clreol(void) { int
xpos, ypos;
xpos =
wherex(); ypos =
wherey();
for (int loop = xpos; loop <= 80;
loop++)
putch(' ');
gotoxy(xpos, ypos); /*
does CONIO restore? */
return; }
RISC OS has no built-in
clear-to-end-of-line, but it's easy to fake. I'm
not sure if the DOS version restores the original cursor
position. I've done so just in case, because if I'm wrong,
subsequent code will probably do it. If I didn't, and it
did, things could look messy. Note that we define
loop within the for() - you'll need a C99
compiler (ISO/IEC 9899:1999) for this!
The early versions of Amélie under RISC OS were badly
stunted by numerous key operations not working, so the
keyboard interfacing has been completely redesigned.
Here goes (remember ctrlgetch is
a global variable):
int
getch(void) { /* A better attempt at emulating the way DOS
returns '0' then control key. */
if (ctrlgetch != 0)
{ int reply =
ctrlgetch; ctrlgetch =
0; return
reply; }
ctrlgetch =
0;
_kernel_swi(XOS_ReadC, &r,
&r);
/* A
'control' key? - WE ONLY SUPPORT THOSE WE USE.
*/ switch
(r.r[0]) {
case 141: /* Alt-T
*/
ctrlgetch =
20;
break;
case 187: /* Alt-X
[Alt-F4 not so easily done...]
*/
ctrlgetch =
45;
break;
case 139: /* Cursor UP
[Page Up]
*/
r.r[0] = 121; /* OS_Byte 121 ->
Direct keyboard scan
*/
r.r[1] = 191; /* 63 EOR &80 - PageUp
scan code
*/
_kernel_swi(XOS_Byte, &r,
&r);
if (r.r[1] ==
0xFF)
{
ctrlgetch = 73; /* Page Up
*/
}
else
{
ctrlgetch = 72; /* Cursor Up
*/
}
break;
case 138: /* Cursor
DOWN [Page Down]
*/
r.r[0] =
121;
r.r[1] = 206; /* 78 EOR &80 -
PageDown scan code
*/
_kernel_swi(XOS_Byte, &r,
&r);
if (r.r[1] ==
0xFF)
{
ctrlgetch = 81; /* Page Down
*/
}
else
{
ctrlgetch = 80; /* Cursor Down
*/
}
break;
case 136: /* Cursor
LEFT
*/
ctrlgetch = 0; /* NOT CURRENTLY REQUIRED
*/
break;
case 137: /* Cursor
RIGHT
*/
ctrlgetch = 0; /* NOT CURRENTLY REQUIRED
*/
break;
case 241: /* F1
*/
case 242: /* F2
*/
case 243: /* F3
*/
case 244: /* F4
*/
case 245: /* F5
*/
case 246: /* F6
*/
case 247: /* F7
*/
case 248: /* F8
*/
ctrlgetch = r.r[0] - 182; /* make DOS
Fkey
*/
break;
default: /*
Ignore it
*/
ctrlgetch =
0;
break;
}
if (ctrlgetch !=
0) return
0;
return
r.r[0]; }
Okay... What's going on here is
essentially we are looking for a 'control' keypress,
Alt-key, cursor, etc. When we have such a keypress, we
preserve the value that we want to return and
actually return zero. This allows us to mimic
the DOS behaviour where Alt-X is handled by
two calls to getch() - the first returning
zero and the second returning forty five. For normal
keypresses, we simply return the key ASCII code. The big
switch statement is concerned with looking for the
keys that we're interested in and returning a suitable
DOS-alike code. You will notice that Cursor Left and Right
are listed but do nothing. This is because Amélie
does not use Left/Right - but if it does in the future,
these definitions will save me looking up the key
codes. The only complication is in our cursor mode, the
same codes are returned for
Cursor Up and Page Up; likewise Cursor
Down and Page Down. Therefore it is necessary
to perform a direct scan of the keyboard to tell which it
actually is.
int
kbhit(void) { int
reply;
r.r[0] = 128; /* OS_Byte 128 -> Read buffer status
*/ r.r[1] = 255; /*
255 = keyboard FREE */
_kernel_swi(XOS_Byte, &r,
&r); reply = 255 -
r.r[1];
return
reply; }
This differs
from the DOS library. kbhit() is supposed to return
TRUE or FALSE. We return the number of bytes used in the keyboard buffer. Empty =
0 = False. Anything else is
not-FALSE. This will work so long as comparisons with
kbhit() check against FALSE. If it is a problem,
just insert code to say "if (reply != 0) reply=
1;".
void _setcursortype(int
c) { if (c
== _NOCURSOR)
{
_kernel_swi(XOS_RemoveCursors, &r,
&r); }
if (c == _NORMALCURSOR)
{
_kernel_swi(XOS_RestoreCursors, &r,
&r); }
return; }
Changing the cursor type is not really
necessary under RISC OS as we only ever use this call to set
the cursor on or off. That's easy enough to do...
void sound(int
f) { /*
We don't support sound-until-no-sound,
so just make a beep...
*/
return; }
The DOS sound system works on the
principle of make a sound at frequency 'f' until a
new frequency is supplied or nosound() is called.
The Acorn system works on the principle of make a
sound on channel 'c' frequency 'f' for duration 'd' at
volume 'v'. I say "Acorn" as the BBC micro did it like
this in the early '80s. Anyway, I don't feel like emulating
the DOS system as AmélieEm only uses it for warning
bleeps. Thus, calling VDU7 (tty bell) will suffice.
void
nosound(void) {
return; }
void
gettextinfo(struct text_info
*ti) { /* We don't fully support windows, so
return full screen
info */
ti->winleft =
1; ti->wintop = 1;
ti->winright = 80; ti->winbottom =
25; ti->attribute =
currattr; ti->normattr = currattr; /* how does this differ from 'attribute'?
*/ ti->currmode =
3; /* C80 */
ti->screenheight = 25; ti->screenwidth =
80; ti->curx = cursorx; ti->cury =
cursory;
return; }
This is supposed to return
information on the screen (overall) and the text window,
however the text window is only supported in a rudimentary
manner (at this time) so this function will always
return details of the whole screen. Under RISC OS, the
current mode is fixed to "CO80". This is because we are
returning what we are pretending to be, rather than
the actual RISC OS screen mode.
void window(int left,
int top, int right, int
bottom) {
_kernel_oswrch(28); /*
set text window position */
_kernel_oswrch( (left - 1)
); _kernel_oswrch( (bottom
- 1) ); _kernel_oswrch(
(right - 1) );
_kernel_oswrch( (top - 1)
);
return; }
At this time, we only support enough to
get a text window on-screen, for the 'welcome' display and any
error report boxes.
void gettext(int left,
int top, int right, int bottom, char
*destin) { int
startcol = (left - 1); int
endcol = (right - 1); int
startrow = (top - 1); int
endrow = (bottom - 1); int thiscol =
0; int thisrow =
0; int destoff =
0;
/* Layout of
CONIO block is
"<text><attr><text><attr><text><attr>[etc]".
*/
for (thisrow
= startrow; thisrow <= endrow; thisrow++)
{ for
(thiscol = startcol; thiscol <= endcol;
thiscol++)
{
destin[destoff++] = textread(thiscol,
thisrow);
destin[destoff++] = attrread(thiscol,
thisrow);
} }
return; }
Read from the screen display buffer
(actually, our virtual copy) into the memory block pointed to
by destin.
void puttext(int left,
int top, int right, int bottom, char
*destin) { int
startcol = (left - 1); int
endcol = (right - 1); int
startrow = (top - 1); int
endrow = (bottom - 1); int thiscol =
0; int thisrow =
0; int destoff =
0; int ttxt =
0; int tatr =
0;
for (thisrow =
startrow; thisrow <= endrow; thisrow++)
{ for
(thiscol = startcol; thiscol <= endcol;
thiscol++)
{ ttxt =
destin[destoff++];
tatr =
destin[destoff++];
/* set up the array
*/
textwrite(thiscol, thisrow,
ttxt);
attrwrite(thiscol, thisrow,
tatr);
/* only output to actual screen if NOT
single-stepping - speeds things up
*/
if (single_stepped !=
TRUE)
{
/* now copy to the actual screen
*/
lastforecolour = (tatr &
15);
lastbackcolour = ( (tatr >> 4) & 15
);
/* For speed, we write the VDU data
directly in one go
*/
vdudata[0] = 31; /* cursor position
*/
vdudata[1] =
thiscol;
vdudata[2] =
thisrow;
vdudata[3] = 17; /* foreground colour
*/
vdudata[4] =
lastforecolour;
vdudata[5] = 17; /* background colour
*/
vdudata[6] = (lastbackcolour +
128);
vdudata[7] = ttxt; /* byte to display
*/
r.r[0] =
(int)vdudata;
r.r[1] =
8;
_kernel_swi(XOS_WriteN, &r,
&r);
/* If only VDU stream active, OS_WriteN
uses direct access to
VDU
drivers so it faster than repeated calls to OS_WriteC (and
I
would assume _kernel_oswrch?). Refer to PRM 1-522.
*/
} }
}
return; }
And in this case, we copy from the block
pointed to by destin into screen memory. As RISC OS
works differently, we have to not only paste the data into our
virtual screen, but we also have to write it to the
actual screen. The exception to this is if we are
single-stepping, in which case we disable writes to the
actual screen. This is because on a PC, the screen
update writes, at most 4000 bytes, to the display adaptor's
memory. RISC OS, working graphically, would need to write
around 70K for the same display. Then we have the overheads of
translating the PCish output to a form suitable for RISC OS in
the first place. That's not to say my 40MHz RiscPC is slow
(compared to my 466MHz PC!), it's just that the display
briefly flickers. It's bloody annoying. So I've disabled it.
:-)
void textattr(int
attr) { int ourattr =
(attr & 127); /* mask out "blink"
bit, NOT SUPPORTED */
lastforecolour = (ourattr & 15);
lastbackcolour = ( (ourattr >> 4) & 15 );
textcolor(lastforecolour);
textbackground(lastbackcolour);
return ; /* return
what? previous attributes? */ }
Set the foreground and
background colours at one time.
void
highvideo(void) { /* Push in 'intense' video colours
*/
lastforecolour -=
8; if (lastforecolour <
8) lastforecolour += 8;
/* in case ALREADY bright
*/
return; }
void
lowvideo(void) { /* Push in 'non-intense' video colours
*/
lastforecolour +=
8; if (lastforecolour >
15) lastforecolour -= 8;
/* in case ALREADY dim
*/
return; }
void
normvideo(void) { /*
Restore default colours - dim white on black, like DOS when
booted */
lastforecolour = LIGHTGRAY; lastbackcolour =
BLACK;
textcolor(lastforecolour);
textbackground(lastbackcolour);
return; }
In any case, I don't
think it is used, just added for
completeness.
MODE 46, by the way, is a
16 colour 80x25 mode. This is as close as we have for DOS's
CO80 without poking the video hardware ourselves. It will
appear letterboxed
on multisync/SVGA/etc monitors. I'll look into this
some other time...
I hope this has given an
insight into some of the issues involved in porting the
software to a different platform. Some ports, such as
Linux i386 will be easier than others - if most of
this can't be done with curses(), it should be
possible to call BIOS functions (OS permitting) as the display
hardware is the same. For other systems (Apple, Amiga,
etc), you'll have to swot up on the display driver system
and fake it as best you can. The main places to check are
apisys.c and tracey.c within
AmélieEm. There may be other bits dotted around in
the source, but if you get Tracey to compile, chances
are you'll have the stuff used by the other bits already in
place. In any case, the quickest way to find out what needs to
be done on your system is to either port the above, or just
run a compile and see what it reports as an unknown function
call!
Here, for your
reference, is a listing of the SetPalette
program:
REM This program sets up the display mode,
the REM colour set, and the text style for
AmélieEm. REM It is a separate program so it may be
fully REM customised for different hardware,
differing REM tastes (in colour, etc etc). REM REM
Rick, 2007/08/02 REM
DIM
mblk% 24
REM Set screen mode
to use; is 80x25 (text) SYS
"XOS_SWINumberFromString",,"ScreenModes_ReadInfo"
TO swi% IF (swi% =
&487C0) THEN REM RiscPC - try setting the mode according
to REM our defined mode. If you have not
already REM merged this mode with your base
modefile, REM you should consider doing so; as it
offers REM a higher refresh rate (less flicker)
than REM the standard 640x200 definition
(which REM runs at 60Hz instead of our
90Hz). REM REM IMPORTANT: Some monitors
(AKF60, AKF85 REM according to !MakeModes) may
have some REM problems with the line rate and
experience REM difficulty with ANY 640x200 mode
unless REM it is the internally-letterboxed
version. REM In this case, remove ALL of this code
and REM set "MODE 46" and wish you had a
more REM capable monitor. :-)
mblk%!0 = 1 mblk%!4 = 640
mblk%!8 = 200 mblk%!12 = 2
mblk%!16 = 90 mblk%!20 = -1
SYS "XOS_ScreenMode", 0, mblk%
TO ; f% IF ((f%
AND 1) = 1)
THEN REM Our 'Amélie' mode is not installed,
try REM *any* 640x200 (4bpp)
mode. mblk%!16 =
-1 SYS
"XOS_ScreenMode", 0, mblk% TO ;
f% IF ((f%
AND 1) = 1)
THEN
REM Unexpected error - do it the 'old'
way...
MODE 46
ENDIF ENDIF ELSE
REM Older hardware - set to MODE 46
(640x200) MODE
46 REM If you have a system
that has very old REM display hardware, or a 50Hz
"TV" monitor, REM try using MODE 14
here. ENDIF
REM Black, red, green, yellow, blue, magenta,
cyan, white VDU 19, 0,
16, 0, 0,
0 VDU 19, 1, 16, 255,
0, 0 VDU 19, 2,
16, 0, 255,
0 VDU 19, 3, 16, 255,
255, 0 VDU 19, 4,
16, 0, 0,
255 VDU 19, 5, 16,
255, 0, 255 VDU 19, 6,
16, 0, 255, 255 VDU
19, 7, 16, 255, 255, 255
REM Again, but
half-bright VDU 19, 8,
16, 86, 86, 86 VDU
19, 9, 16, 172, 0,
0 VDU 19, 10, 16, 0,
172, 0 VDU 19, 11, 16,
172, 86, 0 VDU 19, 12,
16, 0, 0,
172 VDU 19, 13, 16, 172, 0,
172 VDU 19, 14, 16, 0, 172,
172 VDU 19, 15, 16, 196, 196,
196
REM Let cursors
return key codes SYS "XOS_Byte",
4, 1 REM Exit, ready for
AmélieEm...
The final topic
to mention is that of paths. Such code used to be in the
form:
#ifdef __MSDOS__ fp =
fopen("EPROM.IMG", "rb"); #else #ifdef
__riscos fp =
fopen("<AmelieEm$Dir>.EpromImage",
"rb"); #else #error No specific OS
support for opening
files. #endif #endif
Now it looks like:
fp =
fopen(ROMPATH, "rb");
Which is a lot tidier. The file
variables are now held within the amem.h
file, as follows:
#ifdef
__riscos // Filenames and paths for
RISC OS #define
ROMPATH
"<AmelieEm$Dir>.EPROMimage" #define
RAMAREA
"<AmelieEm$Dir>.RAMareaImg" #define
ROMAREA
"<AmelieEm$Dir>.ROMareaImg" #define DUMPAREA
"<AmelieEm$Dir>.MemoryImg" #define QUICKSAVE
"<AmelieEm$Dir>.QuickSave" #else // Filenames (path is current directory) for
DOS #define ROMPATH
"EPROM.IMG" #define RAMAREA
"RAMAREA.IMG" #define ROMAREA
"ROMAREA.IMG" #define DUMPAREA
"MEMORY.IMG" #define QUICKSAVE
"QUICK.SAV" #endif
Furthermore, unless
the size and order of integers is "16 bit in x86 byte
ordering
", it is
recommended that the QuickSave and QuickLoad functions use a
special version number so files from another OS cannot be
loaded (unless you wish to specifically support this?). The
standard version of the array is as produced under DOS. At this time, the
DOS versions will count up from 1 (16 bit 'int', 32 bit
'long', msb last (Intel ordering)) and the RISC OS
versions will count down from 255 (32 bit int and long,
msb first (Motorola ordering)
).
Don't forget - if you have
ported 6502asm or AmélieEm, then please send
me a copy (with modified sources). It is
a requirement of the licence agreement... you did
read that blurb didn't you? :-) |