Welcome, Guest. Please login or register.


Author Topic: Converting Speccy programs to Enterprise (Read 468 times)

Offline geco

  • EP addict
  • *
  • Posts: 4954
  • Country: hu
  • OS:
  • Linux Linux
  • Browser:
  • Firefox 52.0 Firefox 52.0
    • View Profile
Converting Speccy programs to Enterprise
« 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.

Offline geco

  • EP addict
  • *
  • Posts: 4954
  • Country: hu
  • OS:
  • Linux Linux
  • Browser:
  • Firefox 52.0 Firefox 52.0
    • View Profile
Re: Converting Speccy programs to Enterprise
« Reply #1 on: 2018.January.01. 19:44:58 »
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.
Code: [Select]
       
       ;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
« Last Edit: 2018.January.01. 19:50:52 by geco »

Offline geco

  • EP addict
  • *
  • Posts: 4954
  • Country: hu
  • OS:
  • Linux Linux
  • Browser:
  • Firefox 52.0 Firefox 52.0
    • View Profile
Re: Converting Speccy programs to Enterprise
« Reply #2 on: 2018.January.01. 19:51:29 »
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
« Last Edit: 2018.January.02. 19:48:21 by geco »

Offline geco

  • EP addict
  • *
  • Posts: 4954
  • Country: hu
  • OS:
  • Linux Linux
  • Browser:
  • Firefox 52.0 Firefox 52.0
    • View Profile
Re: Converting Speccy programs to Enterprise
« Reply #3 on: 2018.January.01. 20:48:56 »
Reading from port 0feh :
Code: [Select]
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:
Code: [Select]
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
Code: [Select]
                   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:
Code: [Select]
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:
Code: [Select]
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:
Code: [Select]
   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)
« Last Edit: 2018.January.01. 21:07:03 by geco »

Offline geco

  • EP addict
  • *
  • Posts: 4954
  • Country: hu
  • OS:
  • Linux Linux
  • Browser:
  • Firefox 52.0 Firefox 52.0
    • View Profile
Re: Converting Speccy programs to Enterprise
« Reply #4 on: 2018.January.02. 21:03:12 »
Writing to port 0feh :

This part is much easier, the important things for us, border handling, and sound generation (beeper)
Code: [Select]
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:
Code: [Select]
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:
Code: [Select]
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:
Code: [Select]
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)

Code: [Select]
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 12­bit 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:
Code: [Select]
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)

Offline geco

  • EP addict
  • *
  • Posts: 4954
  • Country: hu
  • OS:
  • Linux Linux
  • Browser:
  • Firefox 52.0 Firefox 52.0
    • View Profile
Re: Converting Speccy programs to Enterprise
« Reply #5 on: 2018.January.02. 21:42:34 »
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)
Code: [Select]
     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)
Code: [Select]
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.
« Last Edit: 2018.January.02. 22:05:08 by geco »

Online Zozosoft

  • EP addict
  • *
  • Posts: 12913
  • Country: hu
  • OS:
  • Windows 7/Server 2008 R2 Windows 7/Server 2008 R2
  • Browser:
  • Firefox 57.0 Firefox 57.0
    • View Profile
    • http://enterprise.iko.hu/
Re: Converting Speccy programs to Enterprise
« Reply #6 on: 2018.January.02. 21:56:27 »
I use inverse attributes for flash. In most cases this has proper result, for example menu cursors, etc.
« Last Edit: 2018.January.03. 11:39:40 by szipucsu »

Offline geco

  • EP addict
  • *
  • Posts: 4954
  • Country: hu
  • OS:
  • Linux Linux
  • Browser:
  • Firefox 52.0 Firefox 52.0
    • View Profile
Re: Converting Speccy programs to Enterprise
« Reply #7 on: 2018.January.06. 14:19:37 »
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:

 -   Multi­function "3 tones + noise" stereo sound generator.
 -   Memory paging.
 -   Address decoding for on­board 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 tri­state buffers.
 -   1MHz system clock.
 -   Z80 wait state generator.

The DPC Sound Chip has 22 internal registers, 17 of which are write­only. 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 write­only 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 tri­state 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 7­bit. The 7­bit PN can also be exchanged for a variable length 17/15/11/9­bit 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 7­bit PN counter. The resulting noise is then passed through high­pass and low­pass 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 6­bit write­only registers (one for each sound) which are multiplexed onto an external 6­bit 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 6­bit D/A outputs, when they will constantly output the valuesin tone channel 0 amplitude registers. This is controlled by 2 bits in the write­only 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 tri­stated 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 8­bit 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 flip­flops 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 switch­on 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 write­only system configuration register is used to set the system for 16/64K on­board 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 12­bit 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 4­bit polynomial counter distortion.
          10 = Enable 5­bit polynomial counter distortion.
          11 = Enable 7­bit 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 = 17­bit.
          01 = 15­bit.
          10 = 11­bit.
          11 = 9­bit.
    b4    1 = Swop 17­bit and 7­bit 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    On­board 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.
« Last Edit: 2018.January.06. 14:34:32 by geco »

Offline geco

  • EP addict
  • *
  • Posts: 4954
  • Country: hu
  • OS:
  • Linux Linux
  • Browser:
  • Firefox 52.0 Firefox 52.0
    • View Profile
Re: Converting Speccy programs to Enterprise
« Reply #8 on: 2018.January.06. 15:25:15 »
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 7­bit polynomial counter distortion) has to be written to the selected channel, and 10h (Swop 17­bit and 7­bit 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 emulation routine, all of us used this routine for CPC and Speccy128 conversions.

Memory paging examples:
Code: [Select]
;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
Code: [Select]
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
Code: [Select]
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.
« Last Edit: 2018.January.06. 16:40:20 by geco »