keenerb
Veteran Member
I haven't forgotten about the LS06 circuit test, I just haven't had time to get to it.
I know this is a lame way to patch, but with my limited knowledge i can't do better ATM. It works though!
Usually such checks will have some kind of conditional jump somewhere. Eg, "je/jne enableTandySound". The easiest way to patch it is to replace the conditional jump with a jump that always goes to the correct address for Tandy sound. So either you need to replace the je/jne (or whatever conditional jump they use) opcode with the opcode for 'jmp', or you need to remove the jump altogether, so it is never taken (depending on which way the logic needs to go). In which case you overwrite the jmp opcode and its operand with nop instructions.
seg000:760D mov ax, 0FC00h
seg000:7610 mov es, ax
seg000:7612 assume es:nothing
seg000:7612 cmp byte ptr es:0, 21h ; '!'
seg000:7618 mov ax, 5444h
seg000:761B jz short loc_7632
seg000:761D mov ax, 0F000h
seg000:7620 mov es, ax
seg000:7622 assume es:nothing
seg000:7622 xor bh, bh
seg000:7624 mov bl, es:0FFFEh
seg000:7629 not bl
seg000:762B shl bx, 1
seg000:762D mov ax, cs:[bx+75EEh]
loc_7632: ; CODE XREF: sub_75F6+25j
seg000:7632 xchg ah, al
seg000:7634 mov word_B440, ax
seg000:7637 xor ax, ax
seg000:7639 mov es, ax
seg000:763B assume es:seg000
seg000:763B mov es:word_90, offset loc_764B
seg000:7642 mov es:word_92, cs
seg000:7647 pop es
seg000:7648 assume es:nothing
seg000:7648 pop bx
seg000:7649 pop ax
seg000:764A retn
seg000:764A sub_75F6 endp
Replace the byte at offset 781B (74) with EB. This changes the instruction from jz to jmp.
Case Mem[$FFFF:$000E] Of
$FF:if mem[$f000:$c000]=$21
then machStr:='Tandy 1000'
else machStr:='PC';
$FE,
$FB:machStr:='PC/XT';
$FD:machStr:='PCjr';
$FC:machStr:='PC/AT';
$FA:machStr:='PS/2 Model 30';
$F9:machStr:='PS/2 Convertible';
$F8:machStr:='PS/2 Model 90/95?';
$9A:machStr:='Compaq XT or Compaq Plus';
$2D:machStr:='Compaq PC or Compaq Deskpro';
$30:machStr:='Sperry PC';
$E9:machStr:='Peacock XT';
$00:machStr:='AT&T 6300/Olivetti M24';
seg000:8CCC sub_8CCC proc near ; CODE XREF: sub_7F92+40p
seg000:8CCC push ax
seg000:8CCD push bx
seg000:8CCE push es
seg000:8CCF mov ax, 40h ; '@'
seg000:8CD2 mov es, ax
seg000:8CD4 assume es:nothing
seg000:8CD4 mov al, es:10h
seg000:8CD8 rol al, 1
seg000:8CDA rol al, 1
seg000:8CDC and al, 3
seg000:8CDE inc al
seg000:8CE0 mov byte_B688, al
seg000:8CE3 push ds
seg000:8CE4 mov cx, 6
seg000:8CE7 mov si, 8CBEh
seg000:8CEA mov ax, cs
seg000:8CEC mov ds, ax
seg000:8CEE assume ds:seg000
seg000:8CEE mov di, 0FFEAh
seg000:8CF1 mov ax, 0F000h
seg000:8CF4 mov es, ax
seg000:8CF6 assume es:nothing
seg000:8CF6 repe cmpsb
seg000:8CF8 pop ds
seg000:8CF9 assume ds:dseg
seg000:8CF9 mov ax, 4942h
seg000:8CFC jz short loc_8D2B
seg000:8CFE mov ax, 0FC00h
seg000:8D01 mov es, ax
seg000:8D03 assume es:nothing
seg000:8D03 cmp byte ptr es:0, 21h ; '!'
seg000:8D09 mov ax, 4F54h
seg000:8D0C jz short loc_8D2B
seg000:8D0E mov ax, 0F000h
seg000:8D11 mov es, ax
seg000:8D13 assume es:nothing
seg000:8D13 xor bh, bh
seg000:8D15 mov bl, es:0FFFEh
seg000:8D1A not bl
seg000:8D1C cmp bx, 3
seg000:8D1F jbe short loc_8D24
seg000:8D21 mov bx, 0
seg000:8D24
seg000:8D24 loc_8D24: ; CODE XREF: sub_8CCC+53j
seg000:8D24 shl bx, 1
seg000:8D26 mov ax, cs:[bx-733Ch]
seg000:8D2B
seg000:8D2B loc_8D2B: ; CODE XREF: sub_8CCC+30j
seg000:8D2B ; sub_8CCC+40j
seg000:8D2B xchg ah, al
seg000:8D2D mov word_B67C, ax
seg000:8D30 xor ax, ax
seg000:8D32 mov es, ax
seg000:8D34 assume es:seg000
seg000:8D34 mov es:word_90, offset byte_8D6A
seg000:8D3B mov es:word_92, cs
seg000:8D40 pop es
seg000:8D41 assume es:nothing
seg000:8D41 pop bx
seg000:8D42 pop ax
seg000:8D43 retn
seg000:8D43 sub_8CCC endp
Do you guys (those experienced with 8088 assembly) know the opcodes by head or do you often check listings or cheat sheets?
The early way of checking what system model you're running on was to check the model byte at F000:FFFE. However, a Tandy 1000 fakes the model byte for a PC, so you usually have to do some additional digging and check the first byte of the BIOS ROM to see if it matches what you're expecting (in the case of Tandy 1000, 21h). Here's what typical code did circa 1986:
This wasn't sustainable, so int 15/AH=C0 was supported from 1987-ish onwards that tried to give you more information.Code:Case Mem[$FFFF:$000E] Of $FF:if mem[$f000:$c000]=$21 then machStr:='Tandy 1000' else machStr:='PC'; $FE, $FB:machStr:='PC/XT'; $FD:machStr:='PCjr'; $FC:machStr:='PC/AT'; $FA:machStr:='PS/2 Model 30'; $F9:machStr:='PS/2 Convertible'; $F8:machStr:='PS/2 Model 90/95?'; $9A:machStr:='Compaq XT or Compaq Plus'; $2D:machStr:='Compaq PC or Compaq Deskpro'; $30:machStr:='Sperry PC'; $E9:machStr:='Peacock XT'; $00:machStr:='AT&T 6300/Olivetti M24';
The full system detection code I adapted from a bazillion sources is here, if you're curious: https://github.com/MobyGamer/TOPBENCH/blob/master/TOPB_DET.PAS
without knowing what the 6 bytes are in offset 8cbe, i can't advise what it is checking for.
90 8a 46 ff 98 3d
Hi Trixter, I don't know if you have another thread for your TOPBENCH program, so I'm writing here.
It seems developers wanted the 3 voice music to run exclusively on the PCjr, so even though the Tandy routine is there they somehow made it irrelevant. This is beyond my abilities. Too bad because the 3 voice music is quite allright.
If the goal is creating an executable that always plays the music, what is wrong with forcing PCjr detection on? IIRC the games don't use PCjr graphics, only the music. (An early PCjr surprise was running Rendevous with Rama on a PCjr and getting 3-voice sound, even though it uses CGA graphics and did not advertise any PCjr support.) Maybe I'm misunderstanding the difficulty you're running into.
seg000:82DC push ax
seg000:82DD push bx
seg000:82DE push es
seg000:82DF mov ax, 40h ; '@'
seg000:82E2 mov es, ax
seg000:82E4 assume es:nothing
seg000:82E4 mov al, es:10h
seg000:82E8 rol al, 1
seg000:82EA rol al, 1
seg000:82EC and al, 3
seg000:82EE inc al
seg000:82F0 mov byte_1D0CE, al
seg000:82F3 mov ax, 0FC00h
seg000:82F6 mov es, ax
seg000:82F8 assume es:nothing
seg000:82F8 cmp byte ptr es:0, 21h ; '!'
seg000:82FE mov ax, 5444h
seg000:8301 jz short loc_18318
seg000:8303 mov ax, 0F000h
seg000:8306 mov es, ax
seg000:8308 assume es:nothing
seg000:8308 xor bh, bh
seg000:830A mov bl, es:0FFFEh
seg000:830F not bl
seg000:8311 shl bx, 1
seg000:8313 mov ax, cs:[bx-7D2Ch]
seg000:75F6 push ax
seg000:75F7 push bx
seg000:75F8 push es
seg000:75F9 mov ax, 40h ; '@'
seg000:75FC mov es, ax
seg000:75FE assume es:nothing
seg000:75FE mov al, es:10h
seg000:7602 rol al, 1
seg000:7604 rol al, 1
seg000:7606 and al, 3
seg000:7608 inc al
seg000:760A mov byte_1B7B8, al
seg000:760D mov ax, 0FC00h
seg000:7610 mov es, ax
seg000:7612 assume es:nothing
seg000:7612 cmp byte ptr es:0, 21h ; '!'
seg000:7618 mov ax, 5444h
seg000:761B jz short loc_17632
seg000:761D mov ax, 0F000h
seg000:7620 mov es, ax
seg000:7622 assume es:nothing
seg000:7622 xor bh, bh
seg000:7624 mov bl, es:0FFFEh
seg000:7629 not bl
seg000:762B shl bx, 1
seg000:762D mov ax, cs:[bx+75EEh]
seg000:8CCC push ax
seg000:8CCD push bx
seg000:8CCE push es
seg000:8CCF mov ax, 40h ; '@'
seg000:8CD2 mov es, ax
seg000:8CD4 assume es:nothing
seg000:8CD4 mov al, es:10h
seg000:8CD8 rol al, 1
seg000:8CDA rol al, 1
seg000:8CDC and al, 3
seg000:8CDE inc al
seg000:8CE0 mov byte_1B688, al
seg000:8CE3 push ds
seg000:8CE4 mov cx, 6
seg000:8CE7 mov si, 8CBEh
seg000:8CEA mov ax, cs
seg000:8CEC mov ds, ax
seg000:8CEE assume ds:seg000
seg000:8CEE mov di, 0FFEAh
seg000:8CF1 mov ax, 0F000h
seg000:8CF4 mov es, ax
seg000:8CF6 assume es:nothing
seg000:8CF6 repe cmpsb
seg000:8CF8 pop ds
seg000:8CF9 assume ds:dseg
seg000:8CF9 mov ax, 4942h
seg000:8CFC jz short loc_18D2B
seg000:8CFE mov ax, 0FC00h
seg000:8D01 mov es, ax
seg000:8D03 assume es:nothing
seg000:8D03 cmp byte ptr es:0, 21h ; '"'
seg000:8D09 mov ax, 4F54h
seg000:8D0C jz short loc_18D2B
seg000:8D0E mov ax, 0F000h
seg000:8D11 mov es, ax
seg000:8D13 assume es:nothing
seg000:8D13 xor bh, bh
seg000:8D15 mov bl, es:0FFFEh
seg000:8D1A not bl
seg000:8D1C cmp bx, 3
seg000:8D1F jbe short loc_18D24
seg000:879A push ax
seg000:879B push bx
seg000:879C push es
seg000:879D mov ax, 40h ; '@'
seg000:87A0 mov es, ax
seg000:87A2 assume es:nothing
seg000:87A2 mov al, es:10h
seg000:87A6 rol al, 1
seg000:87A8 rol al, 1
seg000:87AA and al, 3
seg000:87AC inc al
seg000:87AE mov byte_1B87E, al
seg000:87B1 push ds
seg000:87B2 mov cx, 6
seg000:87B5 mov si, 878Ch
seg000:87B8 mov ax, cs
seg000:87BA mov ds, ax
seg000:87BC assume ds:seg000
seg000:87BC mov di, 0FFEAh
seg000:87BF mov ax, 0F000h
seg000:87C2 mov es, ax
seg000:87C4 assume es:nothing
seg000:87C4 repe cmpsb
seg000:87C6 pop ds
seg000:87C7 assume ds:dseg
seg000:87C7 mov ax, 4942h
seg000:87CA jz short loc_187F9
seg000:87CC mov ax, 0FC00h
seg000:87CF mov es, ax
seg000:87D1 assume es:nothing
seg000:87D1 cmp byte ptr es:0, 21h ; '!'
seg000:87D7 mov ax, 5444h
seg000:87DA jz short loc_187F9
seg000:87DC ; ---------------------------------------------------------------------------
seg000:87DC mov ax, 0F000h
seg000:87DF mov es, ax
seg000:87E1 assume es:nothing
seg000:87E1 xor bh, bh
seg000:87E3 mov bl, es:0FFFEh
seg000:87E8 not bl
seg000:87EA cmp bx, 3
seg000:87ED jbe short loc_187F2
seg000:87EF mov bx, 0
With the exception of Nine Princes in Amber ALL of the above games support 3 voice music on a Tandy 1000.
seg000:8CE3 push ds ;save data segment register
seg000:8CE4 mov cx, 6 ;get ready to compare 6 bytes
seg000:8CE7 mov si, 8CBEh
seg000:8CEA mov ax, cs
seg000:8CEC mov ds, ax ;ds:si = cs:8CBEh which is the
;source of the comparison,
;somewhere in the program code
seg000:8CEE mov di, 0FFEAh
seg000:8CF1 mov ax, 0F000h
seg000:8CF4 mov es, ax ;es:si = F000:FFEA, the
;destination of the comparison
seg000:8CF6 repe cmpsb ;compare CX bytes (6 bytes)
;CMPSB subtracts the source from the destination and
;stores the zero result in the zero flag. If this
;finishes with the zero flag set, all bytes compared were
;identical. If the zero flag is clear, then CX holds
;the location of the byte that didn't match.
seg000:8CF8 pop ds ;restore data segment register
seg000:8CF9 mov ax, 4942h ;ax = 4942h
seg000:8CFC jz short loc_18D2B ;jump if zero flag set
;(meaning, jump if we found
;what we were looking for)
seg000:8CFE mov ax, 0FC00h
seg000:8D01 mov es, ax ;es = FC00h (this is also the
;same as F000h:C000h)
seg000:8D03 cmp byte ptr es:0, 21h ;check first byte of BIOS ROM
;to see if it matches value
;found in Tandy BIOSes
seg000:8D09 mov ax, 4F54h ;ax = 4F54h
seg000:8D0C jz short loc_18D2B ;jump if zero flag set
;(meaning, jump if we found
;what we were looking for)
seg000:8D0E mov ax, 0F000h
seg000:8D11 mov es, ax ;es = F000h
seg000:8D13 xor bh, bh ;bh = 0
seg000:8D15 mov bl, es:0FFFEh ;move es:FFFE (where the model
;byte is stored) into bl
seg000:8D1A not bl ;invert model byte in bl
seg000:8D1C cmp bx, 3 ;see if model byte is now 3
seg000:8D1F jbe short loc_18D24 ;jump if it is 3 or less
seg000:8CBE db 43h ; C
seg000:8CBF db 4Fh ; O
seg000:8CC0 db 4Dh ; M
seg000:8CC1 db 50h ; P
seg000:8CC2 db 41h ; A
seg000:8CC3 db 51h ; Q
seg000:8CC4 db 42h ; B
seg000:8CC5 db 49h ; I
seg000:8CC6 db 54h ; T
seg000:8CC7 db 58h ; X
seg000:8CC8 db 52h ; R
seg000:8CC9 db 4Ah ; J
seg000:8CCA db 54h ; T
seg000:8CCB db 41h ; A