; 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
ÿ€Ÿ$ VÂH‚gºNîŽN®ê”Aí€ ü    0¼