sábado, 7 de julho de 2012

Mistério do Chase HQ para Beta 48

Eduardo Luccas tinha chamado atenção para o Chase HQ adaptado para Beta 48 pelo Otto Frederico. Ao contrário da maioria dos jogos multiloads adaptados, não havia distinção entre os tipos de interface (CAS ou CBI), pois funcionava perfeitamente em ambos. Outro efeito mencionado era que aparentemente o carregamento era mais rápido, com o cabeçote do drive executando menos movimentos.


Depois de examinar um pouco melhor o programa, eu acredito ter desvendado este mistério. As incompatibilidades de adaptações entre CAS e CBI devem-se às diferenças entre as respectivas ROMs, mais especificamente nos endereços das rotinas em linguagem de máquina. Mas o que o Otto tinha feito era incorporar todas as rotinas necessárias na própria rotina de carregamento que elaborou, sem usar nenhuma da ROM. Assim, poderia funcionar independentemente da versão da interface.

O disassembly parcial desta rotina segue-se abaixo:
        CALL L33017         ; Switch on Beta 48 IO ports. I register is saved
                            ;on the top of stack.
        CALL L33070         ; Select side 0 of disk.
        CALL L33049         ; Put drive head on track 0.
        JR   Z,L33005       ; Exit routine if head was not found on track 0.
        LD   HL,23552       ; Address where data from disk will be written.
        LD   DE,0           ; Specify track 0 and sector 0.
        LD   B,9            ; Number of tries if any error.
        CALL L33085         ; Read 1 sector.
        JR   Z,L33005       ; Jump forward if any error.
        LD   A,(25827)
        AND  8
        LD   (33171),A
        LD   DE,23552       ; DE points to first directory entry.
L32970:

        LD   HL,33163       ; HL points to XXXX
        LD   A,(DE)         ; If first byte is 0 (i.e. end of directory)
        AND  A
        JR   Z,L33005       ;jump forward.
        CP   (HL)           ; If first byte of directory entry is
        JR   Z,L32988
L32980:

        EX   DE,HL          ; Add 16 to directory entry pointer (DE), to point
        LD   BC,16          ;to next entry address.
        ADD  HL,BC
        EX   DE,HL
        JR   L32970         ; Repeat comparison.
L32988:

        PUSH DE
        LD   B,6
L32991:

        INC  DE
        INC  HL
        LD   A,(DE)
        CP   (HL)
        JR   Z,L33000
        POP  DE
        JR   L32980
L33000:

        DJNZ L32991
        POP  IX
        AND  A
L33005:

        EX   AF,AF'         ; Save accumulator and flags.
        LD   A,%11000000    ; Disable Beta 48 IO ports.
        OUT  (252),A
        POP  AF             ; Get previous interrupt status.
        JP   PO,L33015      ; Jump if interrupt status was 0,
        EI                  ;or enable interrupt if it was 1.
L33015:

        EX   AF,AF'         ; Restore accumulator and flags.
        RET                 ; Exit from loading routine.
;
; Switch on Beta 48 IO ports. I register is saved on top of stack.
;
L33017:

        EX   AF,AF'         ; Save accumulator and flags.
        LD   A,I            ; Get interrupt status in P/V flag.
        POP  HL             ; Get return address.
        PUSH AF             ; Store P/V flag.
        PUSH HL             ; Store back return address.
        DI                  ; Interrupts are disabled.
        XOR  A              ; Switch on Beta 48 IO ports.
        OUT  (252),A
        EX   AF,AF'         ; Restore accumulador and flags.
        RET                 ; Return.

33029   CALL L33017
        LD   B,(IX+13)
        LD   E,(IX+14)
        LD   D,(IX+15)
        LD   HL,23552
        CALL L33085
        JR   L33005
;
; Position drive head on track 0.
L33049:    

   LD   A,#08          ; Command RESTORE with head load flag set.
        JR   L33057         ; Jump to send command to FDC.
;
; Position drive head on track specified by accumulator.
L33053:

        OUT  (127),A        ; Send track value to FDC.
        LD   A,2#18         ; Command SEEK with head load flag set.
L33057:

        OUT  (31),A         ; Send command to FDC.
L33059:

        IN   A,(255)        ; Read Status Register.
        AND  %10000000      ; Read again, until bit 7 is 0 (until command is
        JR   Z,L33059       ;completed).
        IN   A,(31)         ; Read bit 2 of Status Register (head on track 0).
        AND  000100      ; Zero flag is 0 if head is found on track 0.
        RET                 ; Return.

L33070:

        LD   A,(33170)
        SET  4,A            ; Raise bit 4, i.e. select side 0 of disk.
L33075:

        OUT  (255),A      
        RET
L33078:

        LD   A,(33170)
        RES  4,A            ; Lower bit 4, i.e. select side 1 of disk.
        JR   L33075
;
; Read sector E of track D and write in RAM from address pointed by HL. Flag
;Zero will be high if reading is succeeded.
L33085:

        CALL L33070         ; Select side 0 of disk.
        LD   A,(33171)      ; Fetch number of sides of disk.
        AND  A
        LD   A,D            ; A=track.
        JR   NZ,L33103      ; Jump forward if disk is single sided.
        AND  1
        CALL NZ,L33078      ; Select side 1 of disk if track number is odd.
        LD   A,D            ; Calculate physical track number dividing logical
        SRL  A              ;track number by 2.
L33103:

        CALL L33053         ; Position drive head on track A.
        XOR  A              ; Wait for some time to let drive head stabilizes.
L33107:

        PUSH AF
        XOR  A
L33109:

        DEC  A
        JR   NZ,L33109
        POP  AF
        DEC  A
        JR   NZ,L33107
L33116:

        INC  E              ; Increment disk sector counter to next position.
        LD   A,E          
        CP   17
        JR   C,L33127       ; Jump forward if its value is less than 17,
        LD   E,0            ;but if it is not, reset sector counter and
        INC  D              ;increment disk track counter.
        JR   L33085         ; Jump backward and move head to next track.
; WARNING: this part detects and treats errors, but it is wrong!
L33127:

        CALL L33144         ; Read 1 sector and write to RAM.
        IN   A,(31)         ; Read Status Register of FDC.
        AND  101000
; WARNING: the next instruction had to be JR NZ!
        JR   Z,L33138       ; Jump forward if no error,
        XOR  A              ;but if any, return with Zero flag high.
        RET
L33138:

        DJNZ L33116         ; Decrement counter and try again if it is nonzero.
        LD   A,1            ; Return with Zero flag low if max. number of tries
        AND  A              ;has been exceeded.
        RET
;
; Read 1 sector specified by A from disk and write to RAM, starting from adress
;pointed by HL.
L33144:

        OUT  (95),A
        LD   A,#88          ; Command READ SECTOR with side compare flag high.
        LD   C,127
        OUT  (31),A         ; Send command to FDC.
L33152:

        IN   A,(255)        ; Wait until command is completed or new data byte
        AND  %11000000      ;is available at FDC bus.
        JR   Z,L33152
        RET  M              ; Return if command was completed.
        INI                 ; Read data byte from FDC and write to RAM.
        JR   L33152         ; Repeat.

Um dos problemas desta abordagem é que consome bastante memória (mais de 200 bytes). Pelo jeito o Chase HQ tinha bastante espaço sobrando. Eu não estou certo do motivo do drive fazer menos movimentos, mas talvez na rotina do Otto alguns procedimentos de leitura feitos pela ROM do Beta 48 tenham sido omitidos. Aliás, as rotinas do Beta 48 não são otimizadas, poderiam ocupar menos espaço de memória e serem mais rápidas.

Interessante que eu tive uma ideia parecida ao do Otto recentemente, na adaptação do Typhoon 128, com a rotina Read Sector. Só que, para manter o tamanho da rotina menor possível (cerca de 67 bytes), faço uso de sub-rotinas da ROM, que torna a minha rotina dependente da interface.

Nota: os comentário da listagem acima estão incompletos e podem conter erros. Tome cuidado se for usar as informações apresentadas.

9 comentários:

  1. Legal, Flávio!
    Lembrando também que, recentemente o Woody (SpecEmu), converteu o jogo Outrun para CBI<->CAS, usando algo parecido. Infelizmente eu já não tenho o asm da conversão...mas lembro que usava muitos bytes!

    ResponderExcluir
    Respostas
    1. A adaptação do Woody, pelo que me lembro, usava um truque em que o interpretador de comandos do Beta DOS executasse um comando através do USR 15363. O problema desta abordagem é que praticamente todas as variáveis de sistema do BASIC e do DOS são usadas, além da área de BASIC, variáveis e linha de edição, gastando bastante memória.

      Excluir
  2. Hmm...é verdade, ele usou aquele sistema que coloca o token,":", etc, o que torna uma conversão flex (CAS-CBI), mas em compensação a memória...
    Ainda bem que vc lembrou, pq a minha 'memória' tá precisando de um 'upgrade' :-)

    ResponderExcluir
  3. Não sabia dessa distinção CBI-CAS.
    Flávio, os jogos que você anda convertendo são para qual interface?
    Eu tenho a CBI e alguns estão dando problemas de funcionamento (como o AYGames04 por exemplo).

    Abs.

    ResponderExcluir
    Respostas
    1. A interface CAS foi a primeira a aparecer no Brasil, que era um clone bem fiel da Beta 48 inglesa. Depois Otto modificou e criou a CBI-95. Os demais clones (IDS, Arcade) são similares a CBI.
      As adaptações deveriam funcionar em ambas as interfaces, mas com certeza com CBI, pois eu tenho uma IDS. Que tipo de problema você encontrou no AY Games 04?

      Excluir
  4. Alguns jogos, tipo o GeminiWing carregam mas na hora de jogar aparecem quadradinhos e o micro reseta (acho que esse é da compilação Multiload). No AY04 somente o REX funcionou.
    Pode ser pq uso um emulador de drive com cartão SD. Às vezes esses SDs não funcionam muito bem...

    ResponderExcluir
    Respostas
    1. Realmente é um comportamento estranho, pois testei todos esses jogos no IDS-91 e funcionaram perfeitamente. Eu não possuo emulador de drive, talvez seja esse a fonte dos problemas. Será que mudando de SD não funcionaria? Por experiência própria, já vi muitos cartões de memórias que são bem ruins.

      Excluir
  5. Eu não lembro dessa versão para Beta. Se não me engano acho que o primeiro jogo do Otto a ser adaptado de fita pra Beta foi o R-Type. Um garoto que trabalhou na Arcade chamado Pablo conseguiu tirar a rotina de leitura no jogo e adaptar. Depois quando comecei a trabalhar na Arcade o André e Silvio adaptaram um jogo para Beta e eu comecei a fazer também. Foram vários jogos adaptados. Muita saudade daquele tempo. Chegamos até a usar a Multiface1 como uma espécie de memória externa pra ter espaço para colocar a rotina de leitura dos jogos. Muito maneiro.

    ResponderExcluir
    Respostas
    1. Oi, Marcello. Legal esse depoimento, são histórias que devemos preservar. Se quiser deixar mais depoimentos e memórias, entre em contato.

      Excluir

Seu comentário é bem vindo, mas peço que use este espaço adequadamente.