The mono emulator for the Atari computer
This is the documentation for MONO_EMU.PRG v3.00
Mick West
April 1988
The mono emulator lets you use the high resolution mode on the atari ST, using a normal TV or monitor - i.e., you don't have to fork out ú150 on an Atari SM125 High-rez mono monitor, if you want to run programs such as Signum, PCB designer, various Midi programs, PD utilities etc.
This is the Third major version of the program. The first was printed in Popular Computing Weekley in Vol 6 Nos 43 & 44.
This version has a number of improvements:
- It will now run on both the old and new versions of TOS
- It can (must actually) be run from an AUTO folder
- It works with SNAPSHOT.ACC
- It is twice as fast as versions 1 and 2
- It does not crash if you run it on a mono monitor so you can leave it in the AUTO folder
How to use it
Just copy it ( MONO_EMU.PRG ) into an AUTO folder. Then boot from this disk. It will load automatically and display a short message. There will be a prompt at the bottom of the screen asking you to input a number from 10 to 80. This controls how often the screen is updated and also, how much your program is slowed down by the emulator. An input of 10 gives a rather jerky display, but there is no noticeable decrease in speed. Using 80, is very smooth but the ST runs at about 56% of its normal speed If you just press " Return", you will get the default setting of 40, the ST will run at 75% of its normal speed and the display will be quite smooth enough
After entering the speed, you should be returned to a monochrome desktop with a grey background and little icons. You should now be able to load and run any monochrome program. The mono emulator uses about 34K of memory, so if you have only got 512K of RAM then you may encounter problems with some of the larger programs. This is more likely if you have some desk accessories loaded.
You should use the mono emulator from an AUTO folder. If you simply load the program in the normal way, you will get a squashed half-screen version of the desktop. If you now go up to the Options menu, select "Set Preferences" and then click "OK", you will get the normal mono desktop back, but the menus and the dialog boxes will be squashed. Most odd!
The New Roms
The original mono emulator used lots of ROM addresses and only worked with the right ROM. Version 3.00 only uses one ROM address, used to tell TOS to configure itself to being in mono mode. I have not yet worked out how to do without this routine. If anybody does find a way that will be compatible with all the ROMs, then please do write and tell me.
As the ROM call is different for each version of TOS, the program has to detect which version of TOS is installed and then alter itself accordingly. To do the detecting, it checks the date-of-creation bytes, which are stored at address $FC0018 in the ROM. The program then searches through a table until it finds this date; then takes the address of the ROM routine from the table and inserts it in the program. If the date is not found, it will print a message saying so, and default to the V1.08 TOS though it will then probable crash.
At the moment (11 April 1988) the table only has entries for two TOS's, the old and the new British versions. These have dates of 20/11/85 and 22/4/87, with corresponding ROM routines at $FCA76A and $FCA914. However, if you do not have one of these, do not despair; all is not yet lost. It is not very difficult to change. All you need to do is find the ROM creation date (which is simply whatever is stored in the long word at $FC0018) and then find the corresponding ROM routine.
The ROM routine is called from the XBIOS routine "SETSCREEN" and the easiest way to find it is to set up a bit of code like:
MOVE.W #1,-(SP)
MOVE.L #-1,-(SP)
MOVE.L #-1,-(SP)
MOVE.W #5,-(SP)
TRAP #14
ADD.L #12,SP
RTS
Then use a debugger (I used K-Seka) to single step through the ROM call, until you have got past the JMP (A0) instruction in ROM. Then you should come across a bit of code like:
FC0982 TST.L $0004(A7)
FC0986 BMI $FC098E
FC0988 MOVE.L $0004(A7),$044E(A5)
FC098E TST.L $0008(A7)
FC0992 BMI $FC09A4
FC0994 MOVE.B $0009(A7),$FF8201.L
FC099C MOVE.B $000A(A7),$FF8203.L
FC09A4 TST.W $000C(A7)
FC09A8 BMI $FC09CE
FC09AA MOVE.B $000D(A7),$044C(A5)
FC09B0 BSR $FC0726
FC09B4 MOVE.B $044C(A5),$FF8260.L
FC09BC CLR.W $0452(A5)
FC09C0 JSR $FCA76A.L <--- this is it
FC09C6 MOVE.W #$0001,$000452.L
FC09CC RTS
This is from the old British ROM, in which the number is $FCA76A. The routine should be the same in other TOS's, so you should be able to find it quite easily. If you do have problems, get in touch with me.
When you have got the two numbers, you need to add them to the source code. The table is right at the end. Insert them in the place indicated by the comment, but be sure they are before the line 'DC.L $0,$0'. For example suppose the numbers you find are $02061986 and $FCA822 then you should add the line :
DC.L $02061986,$00FCA822 ; German (or wherever)
If you do find any new ROMs, please sent the numbers to me, so that I can keep up to date.
This really is a tedious way of doing things so if anyone does come up with a way of doing without the ROM call, which is compatible with all ROMs then PLEASE let me know. In the meantime this will have to suffice.
If you want to write to me with any numbers, problems, comments, money (it is shareware you know, and it saved you ú150), or anything else then write to:
MICK WEST
27, LYNTON DRIVE
SHIPLEY.
WEST YORKSHIRE,
BD18 3DJ
ENGLAND
Distribute!
MONO_EMU.S
; The Auto Mono Emulator. V3.00 By Mick West. April 1988.
; Routine to make the ST think it is in mono mode. Needs TOS in ROM
; Makes the system think that there is a mono screen, but actually
; be updating a medium real screen from this under Vblank interrupt
; The XBIOS calls; Physbase,Setscreen and Getrez are revectored.
; Put in an Auto Folder
; Calculates which ROM is in use from the date bytes ($FC0018.L)
; and installs the correct ROM call required from a table.
MOVE.L 4(A7),A0
MOVE.L #$8400,D6 ; 32K for the screen
ADD.L $C(A0),D6 ; plus the usual space
ADD.L $14(A0),D6
ADD.L $1C(A0),D6
MOVE.L D6,-(SP) ; save length of program for later
MOVE.W #4,-(SP)
TRAP #14 ; Get screen Resolution
ADDQ.L #2,SP
CMP.W #2,D0 ; If not high then carry on
BNE NOTHIGH
MOVE.L (SP)+,D6 ; else tidy up the stack
MOVE.W #0,-(SP) ; return ok to,GEM
TRAP #1 ; Then exit back to desktop
NOTHIGH:
CLR.L -(SP)
MOVE.W #32,-(SP)
TRAP #1 ; Enter supervisor mode
ADDQ.L #6,SP
MOVE.L D0,SAVESTACK ; Save the supervisor stack
MOVE.L #MESSAGE,-(SP) ; Address of start of message
MOVE.W #9,-(SP) ; Print startup message
TRAP #1
ADDQ.L #6,SP
MOVE.L $FC0018,D0 ; Get the date bytes from ROM
LEA.L ROMTABLE,A0 ; Get start of rom table
FINDROM:
CMP.L #0,(A0) ; Check if finished table
BEQ WRONGROM ; Jump if no more ROMs
CMP.L (A0),D0 ; Check date bytes
BEQ RIGHTROM ; Jump if they match
ADD.L #8,A0 ; Go to next entry in the table
BRA FINDROM ; and try that
WRONGROM:
MOVE.L #WRONG,-(SP) ; Address of start of message
MOVE.W #9,-(SP)
TRAP #1 ; Print message about crashing now.
ADDQ.L #6,SP
MOVE.W #1,-(SP)
TRAP #1 ; Wait for keypress
ADDQ.L #4,SP
LEA.L ROMTABLE,A0 ; Default to the old british
RIGHTROM:
MOVE.L 4(A0),ROMPOKE+2 ; Insert the ROM routine address
INLOOP:
MOVE.L #INPUT,-(SP)
MOVE.W #9,-(SP) ; Print input message
TRAP #1
ADDQ.L #6,SP
MOVE.B #3,MESSAGE ; Input length = 3
MOVE.L #MESSAGE,-(SP)
MOVE.W #10,-(SP)
TRAP #1 ; Input number
ADDQ.L #6,SP
MOVE.W #40,D0 ; Default = 40
TST.B MESSAGE+1
BEQ DEFAULT ; If len=0
CMP.B #1,MESSAGE+1 ; len of 1 not allowed
BEQ INLOOP
CLR.W D0
MOVE.B MESSAGE+2,D0 ; first digit
SUB.W #48,D0
BLE INLOOP ; Too low
CMP.W #9,D0
BGT INLOOP ; Too High (>100)
MULU #10,D0
CLR.W D1
MOVE.B MESSAGE+3,D1 ; second digit
SUB.W #48,D1
BLT INLOOP ; Too low
CMP.W #9,D1
BGT INLOOP ; Too high
ADD.W D1,D0
CMP.W #80,D0
BGT INLOOP ; Check less than 80
DEFAULT:
MOVE.W D0,SCANPOKE+2
SUB.L A5,A5
MOVE.L $044E(A5),MED ; Set MED
MOVE.L $B8,XBIOSPOKE+2 ; Get the old XBIOS address and
MOVE.L $70,VBLANKPOKE+2 ; VBLANK and insert into new versions
MOVE.L #0,MONOPOS ; Set offset to top of screen
SCANPOKE:
MOVE.W #40,MONOLINES ; Fourty lines per Vblank is default
MOVE.W #0,MONOCOUNT ; Counter is set to zero
LEA XEND,A2 ; A2 = pos of generated code
LEA GEN,A1 ; A1 = pos of instructions to copy
MOVE.W #39,D0 ; Generate the code
GENMOVE1:
MOVE.L (A1),(A2)+ ; Copys 40 of - MOVE.W (A0)+,(A1)+
DBF D0,GENMOVE1 ; and MOVE.W (A2)+,(A1)+
ADDQ.L #4,A1
MOVE.W (A1),(A2)+ ; Move the RTS
MOVE.L A2,D0 ; A2 = start of free memory
ADD.L #512,D0 ; Force it to a 512 byte boundry
AND.L #$FFFFFE00,D0
MOVE.L D0,$044E(A5) ; And that is the monochrome screen
MOVE.L D0,MONO ; Set MONO
MOVE.W #$0001,-(SP) ; Hardware to medium
MOVE.L #-1,-(SP)
MOVE.L #-1,-(SP)
MOVE.W #5,-(SP)
TRAP #14 ; Set medium resolution for hardware
ADD.L #12,SP
MOVE.B #$0002,$00044C.L ; set high resolution for software
MOVE.W #0,$452 ; Turn off VBLANK
ROMPOKE:
JSR $0 ; The only ROM call, set up rez info
MOVE.W #1,$452 ; Turn on VBLANK
MOVE.L #XBIOS,$B8 ; Set up the new XBIOS vector
MOVE.L #VBLANK,$70 ; And the new VBLANK vector
MOVE.L SAVESTACK,-(SP) ; Restore the Supervisor stack
MOVE.W #32,-(SP) ; And go back to User mode
TRAP #1
ADDQ.L #6,SP
MOVE.L (SP)+,D0 ; Tidy stack
CLR.W -(SP) ; Exit ok for GEM
MOVE.L D0,-(SP) ; Length of program + data space
MOVE.W #$31,-(SP) ; terminate and stay resident (TSR)
TRAP #1 ; Finished this AUTO program
; This is the new XBIOS routine
XBIOS:
MOVEM.L A1/A2,-(SP) ; Save A1 and A2
MOVE.L SP,A2 ; A2 = the stack
ADD.L #8,A2 ; offset over A1 and A2
BTST #5,(A2) ; Test if called from user mode
BNE NOTUSER ; Skip if it is
MOVE.L USP,A2 ; Otherwise get A2 = User stack
SUB.L #6,A2 ; Offset it as if it were the SSP
NOTUSER:
MOVE.W $6(A2),D0 ; Get XBIOS instruction code
CMP.W #2,D0 ; If it is _PHYSBASE
BEQ PHYSBASE ; then jump to new PHYSBASE routine
CMP.W #4,D0 ; If it is _GETREZ
BEQ GETREZ ; then jump to new GETREZ routine
CMP.W #5,D0 ; If it is NOT _SETSCREEN
BNE NORM_XBIOS ; Then continue with the normal XBIOS
MOVE.W #-1,16(A2) ; Else alter rez.W to -1 (No change)
MOVE.L 12(A2),D0 ; Get the ploc.L parameter
CMP.L #-1,D0 ; If it is -1
BEQ NORM_XBIOS ; then continue with normal XBIOS
MOVE.L D0,MONO ; Otherwise, new value goes to MONO
MOVE.L #-1,12(A2) ; Set ploc.L to -1 (no change)
BRA NORM_XBIOS ; then norm BIOS deals with lloc.L
PHYSBASE:
MOVE.L MONO,D0 ; Get address of mono screen
MOVEM.L (SP)+,A1/A2 ; Tidy stack
RTE ; Return mono screen location
GETREZ:
MOVE.W #2,D0 ; Pretend we are in mono resolution
MOVEM.L (SP)+,A1/A2 ; Tidy the stack
RTE ; Return code for mono resolution
NORM_XBIOS:
MOVEM.L (SP)+,A1/A2 ; Tidy the stack up
XBIOSPOKE:
JMP $0.L ; And jump into the normal XBIOS
; This is the new VBLANK routine
VBLANK:
MOVEM.L D0-D7/A0-A6,-(SP) ; Save all registers
MOVE.W #$333,$FF8242 ; Set up colours, grey for thin lines
MOVE.W #$333,$FF8244 ; (1 vert mono pixel = 1 grey med pix)
BTST #0,$FF8240 ; Check inverted
BEQ INVERT ; Jump if so
MOVE.W #$777,$FF8240 ; White background (normal)
MOVE.W #$000,$FF8246 ; Black ink
BRA NOINVERT
INVERT:
MOVE.W #$000,$FF8240 ; Black background (inverted)
MOVE.W #$777,$FF8246 ; White ink
NOINVERT:
CLR.L D0
MOVE.B $FF8201,D0 ; Video base high
LSL.L #8,D0 ; times 256
MOVE.B $FF8203,D0 ; Plus video base low
LSL.L #8,D0 ; All times 256
MOVE.L D0,A3 ; Is the address of the Real screen
MOVE.L MONO,A0 ; A0 = virtual mono screen
MOVE.L MED,A1 ; A1 = real medium screen
CMP.L A1,A3 ; Check if the real screen has moved
BEQ MEDOK ; Skip this if not
MOVE.L A3,A0 ; Get the new real screen address
MOVE.L A0,MONO ; Set MONO From this
MOVE.L A1,D0 ; And put the real screen back
LSR.L #8,D0 ; to its origional position
MOVE.B D0,$FF8203
LSR.L #8,D0
MOVE.B D0,$FF8201
MEDOK:
MOVE.L A0,A2 ; A2 = mono start
ADD.L #80,A2 ; plus 80, on to next line
MOVE.L MONOPOS,D2 ; Get position in the screen RAM
ADD.L D2,A0 ; Offset position in mono screen
ADD.L D2,A2 ; And the other mono position
ADD.L D2,A1 ; Offset pos in real medium screen
MOVE.W #10,D1 ; default 10 lines / Vblank
TST.B $43E ; Test flock system variable
BNE COPYMOVE ; Set speed to 10 if using disk drive
TST.B $9BE ; Test if motor on ? (not sure)
BNE COPYMOVE ; Jump if using disk
MOVE.W MONOLINES,D1 ; Otherwise get preset speed
COPYMOVE:
BSR XEND ; combine and move two mono lines
ADD.L #80,A0 ; both need moving down another line
ADD.L #80,A2 ; in the mono screen
ADD.L #160,MONOPOS ; move down one medium/two mono lines
ADD.W #1,MONOCOUNT ; count medium lines dome
CMP.W #200,MONOCOUNT ; Done 200 medium/ 400 mono ?
BNE NOT200 ; if not then skip
MOVE.L #0,MONOPOS ; otherwise reset ram offset
SUB.L #32000,A0 ; MONO position back to top of screen
SUB.L #32000,A1 ; and the same for MEDIUM
SUB.L #32000,A2 ; and the other MONO position
MOVE.W #0,MONOCOUNT ; reset the counter
NOT200:
DBF D1,COPYMOVE ; loop round MONOLINES times
VBLEXIT:
MOVEM.L (SP)+,D0-D7/A0-A6 ; Restore all registers
VBLANKPOKE:
JMP $0.L ; Jump to normal VBLANK routine
; The following bits of code are not called but are used to calculate
; a large chunk of code to combine two mono lines into one medium one.
GEN:
MOVE.W (A0)+,(A1)+ ; Move one Mono line to one Medium
MOVE.W (A2)+,(A1)+ ; line on both colour planes times 40
RTS
EVEN
SAVESTACK: DC.L 0
MONO: DC.L 0 ; Base address of mono screen
MED: DC.L 0 ; Base address of medium screen
MONOPOS: DC.L 0 ; Offset in both screens in bytes
MONOLINES: DC.L 0 ; Pairs of mono lines to do per VBLANK
MONOCOUNT: DC.L 0 ; Count of pairs done so far
XEND: nop ; Position of calculated code
MESSAGE:
DC.B 27,'E','The Mono Emulator - Mick West 1988',13,10
DC.B 'V3.00. Should be in AUTO Folder',13,10,13,10
DC.B 'This is Shareware',13,10
DC.B 'Send Money and Problems to:',13,10
DC.B '27 Lynton Drive,',13,10
DC.B 'Shipley,',13,10
DC.B 'BD18 3DJ',13,10
DC.B 'ENGLAND',13,10,13,10
DC.B 'Feel free to give away copies of this',13,10
DC.B 'But please copy the whole folder',13,10,13,10,0
INPUT:
DC.B 13,10
DC.B 'Enter speed (10 to 80, return = 40) ',0
WRONG:
DC.B 'Sorry I do not recognise this Version of',13,10
DC.B 'TOS, I will carry on and probably crash.',13,10
DC.B 'Please read MONOEMU.DOC. Press a Key',13,10,0
EVEN
; This is a table of ROM creation dates ($FC0018) and the corresponding
; ROM routine to set up the resolution information. If you can add to
; this then please let me know the two numbers.
; The ROM routine is called as part of XBIOS _SETSCREEN routine,
; it is the last routine called. See MONOEMU.DOC for more info.
ROMTABLE:
DC.L $11201985,$00FCA76A ; Old British (1.08)
DC.L $04221987,$00FCA914 ; New British (1.09)
; Insert any more here with comments, leave the following terminator
DC.L 0,0