Enterprise Forever
:UK => Programming => Topic started by: geco on 2018.January.01. 19:07:56
-
Here is a conversion example of Armageddon:
START.SRC is the SJASM source which contains the loader and conversion routine
STARTPAS.SRC is the PASMO source which contains the loader and conversion routine
START is the binary created from SJASM source, STARTPAS is the binary created from PASMO source, the 2 binaries are the same, and can be loaded to Enterprise
ARMAGEDD.ONS is the original Speccy screen file
ARMAGEDD.ONP is the original Speccy code file, both 2 files got from Speccy TAP file, and they are not modified, the loader converts them on the fly.
STARTPAS.BAT is the command batch file what I used to assemble the PASMO source.
ARMAGG.ASM is the disassembly list of Armageddon.
-
Loader:
in most cases during Speccy conversions I make a jump from the entry point of the program to initialization part, it is not needed, but I like keeping codes used by the converted program in the beginning of loader.
;Create macro for EXOS calls, which is the name of "special command" of Enterprise eXpandable Operating System.
macro exos,n
rst 030h
defb n
endm
;next 16 byte is the EXOS file header, 2nd byte specify EXOS header 5, which means the program will be loaded to 100h automatically, 3rd and 4th byte is the length of the code, EXOS header is not counted to File length.
org 00f0h
db 00,05 ;EXOS file header
dw fillen
db 00,00,00,00,00,00,00,00,00,00,00,00
startpr ld sp,0100h ;loader starts at 100h, stack pointer must be set here or interrupts must disabled, because the 1st EXOS interrupt would hang the program, because the stack pointer points to EXOS stack address.
jp contin ;jump to loader initialization
hiba di ;soft reset routine
ld sp,0100h
ld a,0ffh
out (0b2h),a
ld c,40h
exos 0
ld a,01h
out (0b3h), a
ld a,06h
jp 0c00dh
hibalng equ $-hiba
isr push af ;interrupt routine
ld a,30h ;reset video interrupt counter, and activate video interrupt (it has to be done at each interrupt, otherwise Enterprise will remain a forever interrupt loop)
out (0b4h),a ;interrupt register
contisr ld a,04h ;check if any of function key is pressed
out (0b5h),a
in a,(0b5h)
cpl
jr z,nofkey ;function key is not pressed
rlca
jr nc,nof1
ld a,21h ;F1 pressed (cheat on)
jr writevl
nof1 rlca
jr nc,nofkey
ld a,32h ;F2 pressed (cheat off)
writevl ld (6f0ah),a
nofkey pop af
ei
ret
keybrd out (0b5h),a ;select keyboard row (A contains the row 00-09h
in a,(0b5h) ;read a keyboard row
ret
keybrd1 out (0b5h),a ;it is invoked from 1 place and A = 3
in a,(0b5h) ;read keyboard row 3 ( esc , 2 , 3 , 5 , 4 , 6 , 1 , 7)
and 05h ;store only keys 6 and 7
ld c,a
ld a,05h
out (0b5h),a ; b7 b6 b5 b4 b3 b2 b1 b0
in a,(0b5h) ;read keyboard row 5 ( xxx , erase , ^ , 0 , - , 9 , xxx , 8 )
rlca
and 2ah
or c
ret
joystck push hl ;read external joysticks
push bc
ld c,01h ;read ext joy1 without fire
call joystc2
ld h,l
inc c ;read ext joy2 without fire
call joystc2
ld a,l
and h
cpl
ld l,a ;store the states of 2 joyticks
ld a,07h ;read internal joystick
out (0b5h),a
in a,(0b5h) ; alt , enter , int left , hold , int up, int right , int down , stop
rrca
rrca
jr c,nodwn
set 2,l
nodwn rrca
jr c,norgt
set 0,l
norgt rrca
jr c,noup
set 3,l
noup rrca
rrca
jr c,noleft
set 1,l
noleft pop bc
ld c,l
pop hl
ret
joystc2 ld l,0ffh
ld b,04h
joystc1 ld a,c
out (0b5h),a ;bit 0 of each line gives back a value of ext joy
in a,(0b6h) ; line0 line1 line2 line3 line4 line5 line6 line7 line8 line9
rrca ; fire1 up1 dwn1 lft1 rgt1 fire2 up2 dwn2 lft2 rgt2
rl l
inc c
djnz joystc1
ret
joyfire ld a,08h ;check state of fire (for internal joystick space is selected)
out (0b5h),a
in a,(0b5h) ; ins , space , rgt shift, . , / ,",", del , m
cpl
bit 6,a ;check space
ret nz
xor a
out (0b5h),a
in a,(0b6h)
cpl
bit 0,a ;check ext1 joy fire
ret nz
ld a,05h
out (0b5h),a
in a,(0b6h)
cpl
bit 0,a ;check ext2 joy fire
ret
memaddr ld de,0000h ;calculate Nick address of video page, input in A, output in HL
rra
rr d
rra
rr d
ex de,hl
ret
;szeg exos 24
; jp nz,hiba
; ld a,c
; ret
defs 200h-$
epcols db 73,217,219,162,166,93,37,21,73,217,219,162,166;,93,37,21
;data for LPT creation, LPT defines the attributes of screen
;256-255 defines 1 raster line
;14h defines: 2 colour (bit5-6), attribute mode (bit1-3), full vertical resolution (bit4)
;left margin 15 and right margin 2fh defines a 32 character wide screen
;byte 4-5 is filled during building of LPT, Nick address of Attribute memory will be here
;byte 6-7 is filled during building of LPT, Nick address of Bitmap memory will be here
line db 255,14h,15,2fh,0,0,0,0 ;192 (1 raster line for definition of speccy screen)
tabl db 00h,19h,92h,0dbh,24h,6dh,0b6h,0ffh ;here is the bright palette for speccy screen)
sync db 0cah,2,3fh,0,0,0,0,0 ;video synchron starts here
db 00,00,00,00,00,00,00,00 ;39
db 0fdh,0,3fh,0,0,0,0,0
db 00,00,00,00,00,00,00,00 ;3
db 0fch,0,6,3fh,0,0,0,0
db 00,00,00,00,00,00,00,00 ;4
db 0ffh,90h,3fh,20h,0,0,0,0
db 00,00,00,00,00,00,00,00 ;1
db 0fch,2,6,3fh,6,0,0,0
db 00,00,00,00,00,00,00,00 ;4
db 0cah,03,3fh,0,0,0,0,0
db 00,00,00,00,00,00,00,00 ;40 sum 312
hossz equ $-sync
vid ld hl,stack ;request a video memory segment (0fch-0ffh segments)
ld (hl),0
ker exos 24
jr z,namivan
cp 7fh
jp nz,hiba
namivan ex af,af'
ld a,c
cp 0fch
jr nc,nemker
inc hl
ld (hl),c
jr ker
nemker ex af,af'
push bc
push af
vissza ld c,(hl)
exos 25
dec hl
jr z,vissza
pop af
pop bc
cp 7fh
ld a,c ;a contains the page
jp z,hiba
ret
;initialization of RAM Settings, generate Speccy like screen, Border colour, Bias colour, and later conversion routines
contin ld hl,hiba ;set address of soft reset routine
ld (0bff8h),hl
ld bc,100h+28 ;EXOS variable 28
ld d,192 ;set bias 192, this specify colour 8-15 ( it will be used for speccy dark colours )
exos 16
ld bc,100h+27 ;EXOS variable 27
ld d,00h ;set border black
exos 16
;allocate video memory, and get the Nick address of allocated video Segments
call vid ;allocate video segment for screen, and store video segment at SCR+1
ld (scr+1),a
call memaddr ;get Nick address of video segment
ld (vidcim1),hl ;address of screen
call vid ;alloc video segment for LPT
nemhiba out (0b1h),a
out (0b3h),a
cont call memaddr ;get Nick address of LPT segment
ld (vidcim2),hl ;address of screen
;build Speccy like screen (create LPT)
ld a,0c0h ;create lpt, Speccy bitmap will be visible 4000-57ffh, attribute will be visible 5800-5affh
ld de,(vidcim2) ;Nick address of LPT
res 7,d
set 6,d ;convert Nick address to z80 address, since the page paged in into page1
push de
exx
ld de,(vidcim1) ;Nick address of Screen
ld ix,vidcim1
pop hl
inc hl
inc hl
inc hl
inc hl
ld bc,000dh
l1 ex af,af'
exx
ld hl,line ;copy a pixel line of speccy screen ( it will be done 192 times )
ld bc,0010h
ldir
exx
ld (hl),e ;store low value of Nick address of attribute for each pixel line
inc hl
ld a,d
rra
rra
rra
and 03h
or 18h
or (ix+1)
ld (hl),a ;store high value of Nick address of attribute for each pixel line
inc hl
ld (hl),e ;store low value of Nick address of bitmap for each pixel line
inc hl
ld (hl),d ;store high value of Nick address of bitmap for each pixel line
add hl,bc
inc d
ld a,d
and 07h
jr nz,l2
ld a,e
add a,20h
ld e,a
ccf
sbc a,a
and 0f8h
add a,d
ld d,a
l2 ex af,af'
dec a
jr nz,l1
exx
ld hl,sync ;copy remaining LPT data ( EP logo, and VSYNC )
ld bc,hossz
ldir ;end of lpt creation
;Allocate other 32K memory if 48 K speccy program is going to be converted
; call szeg ;allocate a 16K segment and page in it to page2 (it is needed if a 48K speccy program is going to be converted)
; out (0b2h),a
; call szeg ;allocate a 16K segment and page in it to page3 (it is needed if a 48K speccy program is going to be converted)
; out (0b3h),a
scr ld a,00h ;page in screen memory to page1
out (0b1h),a
;load screen file
xor a ;open file A is the channel number (it can be nearly any value, except the reserved channel numbers)
ld de,scrn ;file name
exos 1 ;open file
jp nz,hiba ;if not zero, file could not be opened, jump to soft reset
ld de,4000h ;A channel number (zero), DE target
ld bc,1b00h ;BC length
exos 6 ;load file
xor a ;A channel number
exos 3 ;close channel
;convert attributes of speccy screen
ld hl,5800h
chatt1 call chngatt ;convert screen attributes
ld a,h
cp 5bh
jr nz,chatt1
;activate our LPT (Display Speccy Screen)
di
xor a ;activate our LPT (speccy screen)
ld hl,vidcim2+1
rrd
rlca
rlca
rlca
rlca ;LPT Base pointer = Nick address of LPT / 10h + 0c000h
out (82h),a ;low part of LPT base pointer
dispscr or 0c0h
rrd
out (83h),a ;high part of LPT base pointer
;Load program file int it's place
xor a ;open file
ld de,pr1 ;file name
exos 1
jp nz,hiba ;jump if error
ld de,5b00h ;target address
ld bc,2000h ;lebgth
exos 6 ;load file
xor a
exos 3 ;close file
;Set dave registers for beeper sound play back, and set our interrupt routine (this game did not use interrupts, if a game uses interrupt in most cases it uses IM 2, remove IM 2 interrupt definitions, and IM 2 table, and set up the ISR routine and insert the interrupt code into it.
ld bc,00afh ;reset Dave registers
dave00 out (c),b
dec c
bit 4,c
jr z,dave00
di
ld a,3fh ;sound setting
out (0a8h),a ;set left volume of channel 0 to max
out (0ach),a ;set right volume of channel 0 to max
ld a,30h ;enable interrupt video interrupt
out (0b4h),a
ld a,0ch ;no delay (enable max speed if the code runs and makes I/O in normal RAM (not VRAM))
out (0bfh),a
ld a,0c3h ;setup interrupt (IM 1)
ld hl,isr ;load interrupt call to 0038h
ld (0038h),a
ld (0039h),hl
;Conversion of speccy program is done here
call convert ;convert program, and attributes
;wait a key from row 8 for start the program
ei
waitkey ld a,08h ;wait a key from row 8
out (0b5h),a
in a,(0b5h)
halt
inc a
jr z,waitkey
jp 06c7bh ;start program
scrn db 12,"ARMAGEDD.ONS"
pr1 db 12,"ARMAGEDD.ONP"
vidcim1 dw 0
vidcim2 dw 0
chngatt ;convert speccy attributes to EP attributes
nxtbyte ld a,(hl)
ld de,dark
bit 6,a
jr z,dark1
ld de,bright
dark1 push de
and 07h
add a,e
ld e,a
ld a,(de)
ld c,a
ld a,(hl)
pop de
and 38h
rra
rra
rra
add a,e
ld e,a
ld a,(de)
rla
rla
rla
rla
or c
ld (hl),a
inc hl
ret
bright db 00h,04h,01h,05h,02h,06h,03h,07h
dark db 00h,0ch,09h,0dh,0ah,0eh,0bh,0fh
convert ld hl,69f3h-7*40h ;patch Joystick text to INT/EXT
ld de,joyst
ld c,08h
char2 ld b,06h
char1 push bc
ld a,(de)
ld (hl),a
inc de
ld bc,0008h
add hl,bc
pop bc
djnz char1
push bc
ld bc,0010h
add hl,bc
pop bc
dec c
jr nz,char2 ;end of joystick text patch
ld hl,startc
call convprg ;convert code
ld hl,6c71h ;convert attributes
ld b,09h
convatt push bc
call chngatt
pop bc
djnz convatt
ld hl,6c7ch
call chngatt
ld hl,6d16h
call chngatt
ld hl,6de8h
call chngatt
ld hl,6df7h
call chngatt
ld hl,6e09h
call chngatt
ld hl,6e8dh
call chngatt
ld hl,7034h
call chngatt
ld hl,703ch
call chngatt
ld hl,71cdh
call chngatt
ld hl,73c1h
call chngatt
ld hl,74eeh
call chngatt
ret
convprg ld e,(hl) ;convert code
inc hl
ld d,(hl)
inc hl
ld c,(hl)
inc hl
ld b,(hl)
inc hl
ldir
ld a,(hl)
inc a
jr nz,convprg
inc hl
ld a,(hl)
dec hl
inc a
jr nz,convprg
ret
joyst db 00h,00h,00h,00h,00h,00h
db 7fh,08h,08h,08h,08h,08h ;t
db 42h,24h,18h,18h,24h,42h ;x
db 7eh,40h,7ch,40h,40h,7eh ;e
db 02h,04h,08h,10h,20h,40h ;/
db 7fh,08h,08h,08h,08h,08h ;t
db 42h,62h,52h,4ah,46h,42h ;n 69b3
db 3eh,08h,08h,08h,08h,3eh ;i 69f3 every 8th
startc ;6c6e number of lives remaining, 6c6d number of attacks
dw 6cb6h,6cbfh-6cb6h
ad6cb6 ld a,03h
ad6cb9 call keybrd ;[ 1 ] [ 2 ] [ 3 ] [ 4 ] [ 5 ] 01234
ad6cbb and 7ah
ad6cbd cp 7ah
dw 6cc8h,6cd1h-6cc8h
ad6cc8 ld a,03h
ad6cca call keybrd1 ;[ 6 ] [ 7 ] [ 8 ] [ 9 ] [ 0 ] 43210
ad6ccd and 2fh
ad6ccf cp 2fh
dw 6e06h,02h
and 0f0h ;convert attribute
dw 6ce3h,02h
out (0a7h),a ;music
dw 6ce9h,02h
out (0a7h),a ;music
dw 6e34h,02h
or 08h ;convert attribute to Dark
dw 6e41h,02h
or 08h ;convert attribute to Dark
dw 6e50h,02h
or 08h ;convert attribute to Dark
dw 6e5dh,02h
or 08h ;convert attribute to Dark
dw 6eb1h,6eb5h-6eb1h
bit 3,a ;check attribute
jr z,$+0ah ;6ebdh ;check if a town is damaged
dw 6ec0h,6ec4h-6ec0h
bit 3,a ;check attribute
jr nz,$-0dh ;6eb5h
dw 6f54h,02h
out (0a7h),a ;music
dw 6fdch,02h
out (0a7h),a ;music
dw 6fafh,6fb3h-6fafh
and 08h ;check if attribute is bright
jr nz,$+0eh ;6fbfh
dw 6fe9h,02h
out (0a7h),a ;music
dw 6fefh,02h
out (0a7h),a ;music
dw 7015h,02h
out (0a7h),a ;music
dw 701bh,02h
out (0a7h),a ;music
dw 7055h,02h
ld c,4ah
dw 707eh,02h
out (0a7h),a ;music
dw 708dh,02h
out (0a7h),a ;music
dw 715ah,02h
out (0a7h),a ;music
dw 7168h,02h
out (0a7h),a ;music
dw 7171h,7175h-7171h
and 08h
jr z,$+19h ;718ch
dw 7242h,07h
ld a,01h
call keybrd ;s
bit 5,a
dw 724ch,07h
ld a,00h
call keybrd ;x
bit 5,a
dw 7256h,07h
ld a,00h
call keybrd ;n
bit 0,a
dw 7260h,07h
ld a,08h
call keybrd ;m
bit 0,a
dw 726ch,03h
call joystck
dw 72fch,07h
ld a,01h
call keybrd ;a
bit 6,a
dw 730bh,04h
call joyfire ;fire
nop
dw 73a1h,0fh
ld a,01h
call keybrd ;h
bit 0,a
ret nz
ld a,01h
call keybrd ;s
bit 5,a
dw 74ebh,02h
and 0f0h ;convert attribute
end1 dw 0ffffh
stack
fillen equ $-startpr
end
-
1st Check it's loader and get all load addresses and length to be able to load the programs to Enterprise, and check what else important is done in the loader (usually there is nothing else which is important) and the start address of the program
2nd get the binary files from tap, and make a disassembly file from them, if we have sources this step is not needed.
3rd create the loader, which setup the environment for the program, I will use the loader what I used for Armageddon, now it is simplified, Enterprise Logo has been removed from LPT.
4th check the source code, or disassembly list for PORT I/O 0feh , Speccy ROM calls and convert them
now the program can be started and if everything went fine than it runs but with strange colours
5th convert speccy attributes to EP attributes (we finished if it was a speccy 16K or 48K program)
6th find all memory paging, video memory setting, and AY port I/O and convert them (we finished if it was a 128K program)
I will continue with converting of port i/o 0feh
-
Reading from port 0feh :
b7 b6 b5 b4 b3 b2 b1 b0
1 ear 1 keyboard values
In our case only keyboard is important B7 and B5 are always 1, B6 is ear input.
Here is the Speccy Keyboard matrix:
BIN DEC Hex 1 2 4 8 16
b0 b1 b2 b3 b4
11111110 254 FE Caps Z X C V
11111101 253 FD A S D F G
11111011 251 FB Q W E R T
11110111 247 F7 1 2 3 4 5
11101111 239 EF 0 9 8 7 6
11011111 223 DF P 0 I U Y
10111111 191 BF Enter L K J H
11111111 127 7F Space Symbol M N B
and here is the Enterprise keyboard matrix
reading from port 0b5h from 0b6h
b7 b6 b5 b4 b3 b2 b1 b0 b0
128 64 32 16 8 4 2 1 1
line 80H 40H 40H 10H 08H 04H 02H 01H 01h
0 LSHIFT Z X V C B \ N ext1 fire
1 CTRL A S F D G LOCK H ext1 up
2 TAB W E T R Y Q U ext1 down
3 ESC 2 3 5 4 6 1 7 ext1 left
4 F1 F2 F7 F5 F6 F3 F8 F4 ext1 right
5 ERASE ^ 0 - 9 8 ext2 fire
6 ] : L ; K J ext2 up
7 ALT ENTER INTLFT HOLD INTUP INTRGT INTDWN STOP ext2 down
8 INS SPACE RSHIFT . / , DEL M ext2 left
9 ] P @ O I ext2 right
The problems are starting here, on Speccy keyboard reading in most cases is the following:
For 1 key row:
ld a,0feh ;key row of Caps Shift
in a,(0feh)
for all keys
xor a
in a,(0feh)
Often used code to read 1 keyboard row also:
ld bc,0bffeh ;for row of enter
in x,(c) ;where x can be any of the registers
On Enterprise it is 2 bytes longer for 1 key row, and much longer for all keys:
for 1 key row:
get_one_key
ld a,08h ;key row of Right shift
out (0b5h),a
in a,(0b5h) ;the appropriate bit is 0 if the key is pressed
for all key rows: (it will return with Zero flag set if key was pressed, and Zero flag reset if none of keys was pressed)
get_all_keys
push bc
ld b,0ah
readkey
ld a,b
dec a
out (0b5h),a
in a,(0b5h)
inc a
jr z,keypres
djnz readkey
keypres
pop bc
ret
So in above cases reading of 0feh port should be changed to a CALL get_all_keys, or CALL get_one_key instruction, if we modify the binary, if w modify source the situation is much better :)
Reading the joystick on Enterprise:
ld a,01h ;read the status of ext1 up
out (0b5h),a
in a,(0b6h) ;bit0 of A contains the status of ext1 up (bit0 is 0 if it was pressed)
-
Writing to port 0feh :
This part is much easier, the important things for us, border handling, and sound generation (beeper)
b7 b6 b5 b4 b3 b2 b1 b0
x x x Ear Mic Border values
Border conversion
Port 081h is responsible for border values on Enterprise, from bit0 to bit7, 256 colours can be used:
b7 b6 b5 b4 b3 b2 b1 b0
grn0 red0 blu1 grn1 red1 blu2 grn2 red2
where grn0 is the darkest shade of green, and grn2 is the lightest shade of green.
Speccy has only 8 border colours which is the dark colour palette, our Speccy screen uses Bias 192 for dark colours (192/8 = 18h is written to Enterprise 80h Bias port), we use to set Bias by EXOS calls, because b7 of port 80h turn on/off the internal speaker.
- Colour8-15 on EP dark colours, and colour0-7 bright colours, opposite than on Speccy, but this most similar to the Speccy palette )
Port 80h:
b7 b6 b5 b4 b3 b2 b1 b0
on/off speaker external colour input Bias values
Based on above the Speccy border conversion table to Enterprise:
Speccy - Enterprise
00 00h Black
01 0c4h (196) Blue
02 0c1h (193) Red
03 0c5h (197) Magenta
04 0c2h (194) Green
05 0c6h (198) Cyan
06 0c3h (195) Yellow
07 0c7h (199) Gray (dark white)
Sound conversion:
Enterprise has a 6 bit D/A converter on 0a7h port (Dave control port), which control bits are in the same position as of Speccy 0feh port, therefore in sound generation routines 0feh port writes should be changed to 0a7h port writes, and need a small initialization in the loader (it has to be done after EXOS interrupt is changed to our own interrupt, because EXOS interrupt writes all Dave registers in each Video interrupt)
initialization code at the end of the loader
xor a
out (0a7h),a ;Dave control register
ld a,3fh
out (0a8h),a ;Dave left volume register for channel0 (Dave volume registers are 6 bit registers, now we set channel0 to maximum volume)
out (0ach),a ;Dave right volume register for channel0
There is other solution also for simple port substitution, but it's sound is much quiter, change all sound generation 0feh port writes to any of the volume registers, and set up it's corresponding 12bit down counter of tone period to 0001h.
There are other solutions also, but those make sound playback a bit slower, and more modifications are needed, if the conversion is done by source then the following is the best I think:
initialization code at the end of the loader
ld a,18h
out (0a7h),a ;Dave control register
and change out (0feh),a to the following
out (0a8h),a ;Dave left volume register for channel0 (Dave volume registers are 6 bit registers, now we set channel0 to maximum volume)
out (0ach),a ;Dave right volume register for channel0
In most cases port 0feh as output is used 1 thing in the same time, the "problems" are starting when it is used for border effect and sound generation in the same time, it can be converted with a bit longer code on Enterprise, because 2 port writes should be used, and probably a small colour conversion for border colours.
Spectrum program using ROM routines:
We have to check disassembly list of binary, or source code (if we have) whether the program uses Speccy ROM calls, if yes, then we can hope only small routines and not so much ( in this case I created the source of the speccy ROM routines into the loader and changed the address of ROM calls in the program during conversion phase), otherwise the conversion need too much effort.
Other very ugly stuff, there are quite huge amount of speccy programs which writes data to 0000-4000h area into Speccy ROM, it does not cause any problem on Speccy, but in our conversion it can, or it will :ds_icon_cheesygrin: so this has to be checked also, and should be solved to avoid overwriting our important code.
( the easiest way to check it with the emulator if the program writes into 0000-3fffh which is important for us)
-
Converting Attributes:
Attribute conversion table for this palette settings: ( unfortunately flash can not be emulated with fix attribute conversion, so flash bit set has the same result in this table as flash bit reset, inverse attribute can be used for flash bit set if needed)
0 1 2 3 4 5 6 7 8 9 a b c d e f
00 00 0C 09 0D 0A 0E 0B 0F C0 CC C9 CD CA CE CB CF
10 90 9C 99 9D 9A 9E 9B 9F D0 DC D9 DD DA DE DB DF
20 A0 AC A9 AD AA AE AB AF E0 EC E9 ED EA EE EB EF
30 B0 BC B9 BD BA BE BB BF F0 FC F9 FD FA FE FB FF
40 00 04 01 05 02 06 03 07 40 44 41 45 42 46 43 47
50 10 14 11 15 12 16 13 17 50 54 51 55 52 56 53 57
60 20 24 21 25 22 26 23 27 60 64 61 65 62 66 63 67
70 30 34 31 35 32 36 33 37 70 74 71 75 72 76 73 77
80 00 0C 09 0D 0A 0E 0B 0F C0 CC C9 CD CA CE CB CF
90 90 9C 99 9D 9A 9E 9B 9F D0 DC D9 DD DA DE DB DF
a0 A0 AC A9 AD AA AE AB AF E0 EC E9 ED EA EE EB EF
b0 B0 BC B9 BD BA BE BB BF F0 FC F9 FD FA FE FB FF
c0 00 04 01 05 02 06 03 07 40 44 41 45 42 46 43 47
d0 10 14 11 15 12 16 13 17 50 54 51 55 52 56 53 57
e0 20 24 21 25 22 26 23 27 60 64 61 65 62 66 63 67
f0 30 34 31 35 32 36 33 37 70 74 71 75 72 76 73 77
I use the following small code to change attribute byte(s) in code: (bright and dark values should not cross 100h border)
Convert one byte:
ld hl,address of attr byte to be converted
call chngatt
Convert more byte (in this example the whole attribute memory:
ld hl,5800h
chatt1 call chngatt ;convert screen attributes
ld a,h
cp 5bh
jr nz,chatt1
chngatt ;convert speccy attributes to EP attributes
nxtbyte ld a,(hl)
ld de,dark
bit 6,a
jr z,dark1
ld de,bright
dark1 push de
and 07h
add a,e
ld e,a
ld a,(de)
ld c,a
ld a,(hl)
pop de
and 38h
rra
rra
rra
add a,e
ld e,a
ld a,(de)
rla
rla
rla
rla
or c
ld (hl),a
inc hl
ret
bright db 00h,04h,01h,05h,02h,06h,03h,07h
dark db 00h,0ch,09h,0dh,0ah,0eh,0bh,0fh
I use the emulator to find most of the Attribute writes in the program by setting 5800-5affw (break on writes to Attribute memory) and check the code for the rest.
I could solve emulation of Flash byte in SPEmu (spectrum simulator), but it converts attributes on the fly third of the screen in one frame, it is not a good solution in a converted program.
-
I use inverse attributes for flash. In most cases this has proper result, for example menu cursors, etc.
-
This part is from the original EXOS book some details added
DAVE Chip (DPC Sound Chip. DPC/Dave/1 Issue 5)
The DPC Sound Chip performs the following functions:
- Multifunction "3 tones + noise" stereo sound generator.
- Memory paging.
- Address decoding for onboard RAM, ROM and cartridge.
- Interrupt system including 1Hz and programmable frequency timer interrupts and two external inputs.
- Reset circuit compatible with Z80 and dynamic RAM.
- I/O strobe signals for use with external octal latches and tristate buffers.
- 1MHz system clock.
- Z80 wait state generator.
The DPC Sound Chip has 22 internal registers, 17 of which are writeonly. 16 of these registers are associated with the sound generation, four R/W registers are for memory management, and one R/W register is used for interrupt control. The last writeonly register is used for setting the overall system configuration. Internal decoding is provided for a further 3 I/O registers, read and write strobes being brought out for use with external latches and tristate buffers on the data bus. Reset clears all 22 internal registers.
The 3 tone generators produce a square wave with frequency programmable from 30Hz to 125KHz which can be modified in various ways:
Distortion can be introduced by using the output frequency to sample H.F. clocked polynomial counters. PN counters which can be selected are 4,5 or 7bit. The 7bit PN can also be exchanged for a variable length 17/15/11/9bit PN counter.
A simple high pass filter is provided on each channel clocked by the output of a different channel.
A ring modulator effect is provided on each channel, with the output of a different channel for it's other input.
The noise channel is normally a 17 bit PN counter clocked from 31KHz, generating a pseudo white noise. The input to this counter can be changed to clock of any of the 3 tone channels, and the PN counter can be reduced in length to 15,11 or 9 bits. This counter can also be exchanged for the 7bit PN counter. The resulting noise is then passed through highpass and lowpass filters and a ring modulator, each controlled by the output of a different tone channel.
The 3 tone generator outputs and the noise generator output are routed to 2 amplitude control circuits (left and right). Each amplitude control consists of four 6bit writeonly registers (one for each sound) which are multiplexed onto an external 6bit D/A resistor network. In it's own time slot each channel outputs the value in it's amplitude register if the tone is high, else zero.
Either or both of the sound output channels may be turned nto 6bit D/A outputs, when they will constantly output the valuesin tone channel 0 amplitude registers. This is controlled by 2 bits in the writeonly sound configuration register. Three further bits may be used to synchronise the tone generators by holding them at a preset count until sync bit goes low.
Memory management consists of four read/write registers which may be output onto A14-A21 pins by selecting the required register with A14', A15'. This provides 256 * 16K pages. These outputs may be tristated with BREQ.
Four latched interrupts are provided, a 1Hz interrupt for time clock applications, an interrupt switchable between 50Hz, 1KHz or the outputs of tone generators 0 or 1, and two external negative edge triggered interrupts. Each interrupt latch has it's own enable and reset controlled by an 8bit write-only register. An attempt to read this register will return the state of the four interrupt latches and two interrupt input pins, and also two flipflops toggling off the timer interrupts. The setting of any interrupt latch will bring IRQ low (open drain). 50Hz/1KHz/tone generator interrupt selection is made by 2 bits in the sound configuration register.
Select signals are generated for ROM, cartridge, video RAM and video I/O. A 1MHz clock output is also provided.
A Z80 is reset provided on RSTO, either on switchon by an external RC network on CAP, or a low going signal on RSTI. The latter generates a 1mS reset pulse synchronised to the falling edge of M1 to prevent loss of data stored in dynamic RAM. THE RSTO output requires an external 74ALS04 inverter to drive the system reset line at the correct speed an inversion.
A writeonly system configuration register is used to set the system for 16/64K onboard RAM, 8/12MHz input clock, and wait states. The wait state generator can be programmed to give no wait states, waits on opcode fetch only, or waits on all memory accesses. Note that no wait is ever generated for access to video RAM, as this would conflict with Z80 clock stretch.
Register Descriptions:
Register0 W 0A0h
b7-b0 Low byte of number to be loaded into 12bit down counter to set period of tone channel 0.
Register1 W 0A1h
b3-b0 High nybble of above. Fout = 125,000 / (n+1) Hz.
b5,b4 00 = Pure tone.
01 = Enable 4bit polynomial counter distortion.
10 = Enable 5bit polynomial counter distortion.
11 = Enable 7bit polynomial counter distortion.
b6 1 = Enable High Pass Filter using tone channel 1 as clock.
b7 1 = Enable Ring Modulator with tone channel 2.
Register2 W 0A2h
As Regster0 but for tone channel 1.
Register3 W 0A3h
As Register1 but for tone channel 1, except:
H.P.F. uses tone channel 2.
R.M. uses noise channel.
Register4 W 0A4h
As Register0 but for tone channel 2.
Register5 W 0A5h
As Register1 but for tone channel 2, except:
H.P.F. uses noise channel.
R.M. uses tone channel 0.
Register6 W 0A6h
b1,b0 Select noise clock frequency:
00 = 31.25 KHz
01 = tone channel 0.
10 = tone channel 1.
11 = tone channel 2.
b3,b2 Select polynomial counter length:
00 = 17bit.
01 = 15bit.
10 = 11bit.
11 = 9bit.
b4 1 = Swop 17bit and 7bit polynomial counters.
b5 1 = Enable low pass filter on noise using tone channel 2 as clock
b6 1 = Enable high pass filter on noise using tone channel 0 as clock
b7 1 = Enable ring modulator with tone channel 1.
Register7 W 0A7h
b0 Sync for tone channel 0. (1 = hold at preset, 0 = run).
b1 Sync for tone channel 1.
b2 Sync for tone channel 2.
b3 1 = Turn L.H. audio output into D/A, outputting value in Register8 (0A8H).
b4 1 = Turn R.H. audio output into D/A, outputting value in Register12 (0ACH).
b6,b5 Select interrupt rate:
00 = 1KHz.
01 = 50Hz.
10 = Tone generator 0. f = 250,000/(n+1)
11 = Tone generator 1.
b7 Undefined.
Register8 W 0A8h
b5-b0 Tone channel 0 L.H. amplitude, also value output to L.H. D/A if Register7.b3 = 1. (0A7h.b3)
b7,b6 Undefined.
Register9 W 0A9h
b5-b0 Tone channel 1 L.H. amplitude.
b7,b6 Undefined.
Register10 W 0AAh
b5-b0 Tone channel 2 L.H. amplitude.
b7,b6 Undefined.
Register11 W 0ABh
b5-b0 Noise channel L.H. amplitude.
b7,b6 Undefined.
Register12 W 0ACh
b5-b0 Tone channel 0 R.H. amplitude, also value output to R.H. D/A if Register7.b4 = 1. (0A7h.b4)
b7,b6 Undefined.
Register13 W 0ADh
b5-b0 Tone channel 1 R.H. amplitude.
b7,b6 Undefined.
Register14 W 0AEh
b5-b0 Tone channel 2 R.H. amplitude.
b7,b6 Undefined.
Register15 W 0AFh
b5-b0 Noise channel R.H. amplitude.
b7,b6 Undefined.
Register16 R/W 0B0h (set segment to page0 0000-3fffh)
b7-b0 Page register output to A21-A14 if A15',A14' = 00
Register17 R/W 0B1h (set segment to page1 4000-7fffh)
b7-b0 Page register output to A21-A14 if A15',A14' = 01
Register18 R/W 0B2h (set segment to page2 8000-bfffh)
b7-b0 Page register output to A21-A14 if A15',A14' = 10
Register19 R/W 0B3h (set segment to page3 c000-ffffh)
b7-b0 Page register output to A21-A14 if A15',A14' = 11
Register20 W 0B4h
b0 1 = Enable 1KHz / 50Hz / Tone Generator interrupt.
b1 1 = Reset 1KHz / 50Hz / Tone Generator interrupt latch.
b2 1 = Enable 1Hz interrupt.
b3 1 = Reset 1Hz interrupt latch.
b4 1 = Enable Video Interrupt
b5 1 = Reset Video interrupt latch
b6 1 = Enable NET
b7 1 = Reset NET latch
(Video Interrupt bit (VINT) can not be set in LPT in 2 next line of LPT, because interrupt occurs in the line in LPT when interrupt bit not set (VINT)
Register20 R 0B4h
b0 1KHz / 50Hz / Tone Generator divider state. (f int/2 square wave). (1 = set)
b1 1KHz / 50Hz / Tone Generator latch state (1 = set)
b2 1Hz divider. (0.5 Hz square wave) state (1 = set)
b3 1 = 1Hz latch state (1 = set)
b4 1 = Video Interrupt divider state (1 = set)
b5 Video Interrupt latch state (1 = set)
b6 1 = NET divider state (1 = set)
b7 NET latch state (1 = set)
Register21 W 0B5h
b0-b3 select row of keyboard matrix
b4 STROBE output to printer port
b5 turn off tape sound (if bit is 1 then there is not tape sound)
b6 turn on REM1 (tape remote controller 1) (if bit is 1, REM1 is on)
b6 turn on REM2 (tape remote controller 2) (if bit is 1, REM2 is on)
Register21 R 0B5h
b0-b7 read actual state of the selected keyboard row (Table is 2 posts below)
Register22 W 0B6h
b0-b7 printer data
Register22 R 0B6h
b0 J column of external joystick (Table is 2 posts below)
b1 K column of external joystick
b2 L column of external joystick
b3 READY input from printer port (0 signs if the printer is ready for communication
b4 Serial/Net Data in
b5 Serial/Net Status in
b6 Tape input level (1 if no input, 0 is high level, 1 is low level)
b7 Tape data input (1 if no input)
(because of J,K,L lines, Enterprise is capable to handle joysticks with more buttons (max 30))
Register23 W 0B7h
b0 Serial/Net data out
b1 Serial/Net status out
Register23 R 0B7h
not used
Register31 R 0BFh
b0 Onboard RAM. 0 = 64K, 1 = 16K. (if 1 selected, only 0ffh segment is going to be decoded as Video Memory)
b1 Input clock frequency. 0 = 8MHz, 1 = 12MHz.
Dave frequency modifier: 4 MHz / 2*8 = 250Khz (by default), modified: 4MHz / 24 = 166 KHz
b3,b2 00 = Wait on all memory access except video RAM.
01 = Wait on M1 only, except video RAM.
10 = No waits.
11 = No waits.
-
Programming of Dave as sound chip is similar to AY, programming tone generators are similar, only the equation differs:
Speccy: 110625 / n
EP: 125000/(n+1)
Be careful b7-b5 on ports 0a1h, 0a3h, 0a5h sets various effects on EP, which does not have any effect on speccy.
Unfortunately Dave does not have envelope generator, but it can be easily emulated in a 1KHz interrupt.
As you can see Dave has only 1 noise generator which frequency can not be changed freely, but this does not cause any problem, because tone generators can be used as noise also, just 30h (Enable 7bit polynomial counter distortion) has to be written to the selected channel, and 10h (Swop 17bit and 7bit polynomial counters) should be written to channel 0a6h, there is one problem, which could not be emulated, when sound and noise appears on the same channel in the same time on the AY.
Istvanv wrote a very good AY (https://enterpriseforever.com/programozas/tegyuk-rendbe-az-ep-programokat/msg18892/#msg18892) emulation routine, all of us used this routine for CPC and Speccy128 conversions.
Memory paging examples:
;Request a memory segment and page in into page1 (4000-7fffh) and store it.
EXOS 24 ( rst 30h , db 24)
ld a,c
out (0b1h),a
ld (seg1),a
;page in 0ffh segment (system segment) to page1(4000-7fffh) and page2(8000-bfffh)
ld a,0ffh
out (0b1h),a
out (0b2h),a
;reset our segment to page1:
seg1 ld a,00h
out (0b1h),a
Interrupt handling:
Mainly Video interrupt is used only in programs, if I know well EXOS using Video interrupt and 1Hz interrupt in the same time, and there are some programs which are used video interrupt and Dave programmable interrupt in same time. Reading of 0B4h port has an advantage on Enterprise, interrupt state can be read even if the interrupt is disabled by Z80, but please aware if you send 00h to 0b4h port all interrupts are disabled even interrupt is enabled by Z80.
Other very important point: the used interrupt latch has to be reset when that interrupt occure in each occurance, if it does not happen, Enterprise can remain a forever interrupt loop, which can be solved by a soft, or a hard reset :D
Setup a Video interrupt:
ld a,30h
out (0b4h),a
di
ld a,0c3h
ld hl,intrut
ld (0038h),a
ld (0039h),a
;checking if video interrupt reached in disabled interrupt state:
waitvid
in a,(0b4h)
and 10h
jr z,waitvid
. . .
intrut
push af
ld a,30h
out (0b4h),a
;code of interrupt routine is here
pop af
ei
ret
Setup a video interrupt and a 3906Hz interrupt in the same time
ld a,1fh
out (0a0h),a
xor a
out (0a1h),a ;frequency = 250,000 / (31+1)
ld a,40h
out (0a7h),a ;interrupt frequency will be: Tone generator 0. f = 250,000/(n+1)
di
ld a,33h
out (0b4h),a
ld a,0c3h
ld hl,intrut1
ld (0038h),a
ld (0039h),a
ei
. . .
intrut1
push af
in a,(0b4h)
and 02h
jr z,vidint
ld a,13h
out (0b4h),a
;code of 3906Hz interrupt routine is here
pop af
ei
ret
vidint
ld a,31h
out (0b4h),a
;code of video interrupt routine is here
pop af
ei
ret
0bfh port waits
By default EXOS using 04h (Wait on M1 only, except video RAM.) which means when a program loaded 0bfh port setting remains 04h so the program will not run with full speed, therefore if full 4 MHz is planned to be used by a program which is not running in video memory 0ch should be sent to 0bfh port.
Originally Written by Istvnanv, it was translated by google and me :D
Waiting on a standard 4 MHz machine is 1 Z80 cycle, on turbo (6 or 7.119 MHz) are 2 Z80 cycles. Bits 1 have no effect on the amount of waiting. These settings does not have effect for Video RAM, because there have to synchronize the access to 889846 Hz. On a 4 MHz machine, it is easy to approach - not too accurately - that 1,5 should be added to the number of Z80 cycles elapsed between the two video RAM or video I / O port (since the NICK 80h-8Fh ports are in this case), then if the result is not divisible by 4.5, round up to divide. The wiat is difference between this and the number of cycles of the original (before the addition of 1.5) in 0.5 Z80 cycle units. However, it is important the memory operations when occurs exacltly in an instruction:
M1 reading: total duration of 4 cycles, reading time within this 2.0 cycle,
normal memory reading and writing: total duration of 3 cycles, memory operation time of 2.5 cycles,
I / O port read and write: 4 cycles, including 3.5 cycles, the actual I / O operation.