├── PLAYCD.ASM ├── PRINTF.INC └── make.bat /PLAYCD.ASM: -------------------------------------------------------------------------------- 1 | 2 | ;*** playcd - plays track(s) of an audio CD. 3 | ;*** Public Domain. 4 | ;*** main purpose of this program is to test the CD/DVD driver's 5 | ;*** support for Audio CDs. 6 | ;*** 7 | ;*** to create the binary enter: 8 | ;*** jwasm -mz playcd.asm 9 | ;*** 10 | ;*** Masm v6+ should work as well; you'll need a 16-bit 11 | ;*** OMF linker then (recommended: jwlink or OW WLINK): 12 | ;*** ml -c playcd.asm 13 | ;*** jwlink format dos file playcd.obj 14 | 15 | .286 16 | .model small 17 | .dosseg 18 | .386 19 | 20 | cr equ 13 21 | lf equ 10 22 | 23 | ;--- CStr(): macro to define a string constant 24 | 25 | CStr macro text:VARARG 26 | local sym 27 | .const 28 | sym db text,0 29 | .code 30 | exitm 31 | endm 32 | 33 | ;--- DOS device driver request header 34 | 35 | reqhdr struct 36 | len db ? 37 | subunit db ? 38 | cmd db ? 39 | status dw ? 40 | res1 dd ? 41 | res2 dd ? 42 | reqhdr ends 43 | 44 | ;--- values for reqhdr.cmd 45 | IOCREAD equ 03h 46 | PLAY equ 84h 47 | STOP equ 85h ;stop playing 48 | 49 | cmd03 struct ;read ioctl 50 | reqhdr <> 51 | mdesc DB ? ; block devices: Media descriptor byte from BPB 52 | taddr DD ? ; Transfer address 53 | numbyt DW ? ; call: # bytes to transfer; return: # bytes transfered 54 | ;start DW 0 ; Starting sector number 55 | ;volid DD 0 ; DWORD ptr to requested vol ID if error 0FH 56 | cmd03 ends 57 | 58 | COMMENT ^ 59 | +-----------------------------------------------------------------+ 60 | | read ioctl input codes: | 61 | | Code to Transfer Function | 62 | | | 63 | | 0 5 Return Address of Device Header | 64 | | 1 6 Location of Head | 65 | | 2 ? Reserved | 66 | | 3 ? Error Statistics | 67 | | 4 9 Audio Channel Info | 68 | | 5 130 Read Drive Bytes | 69 | | 6 5 Device Status | 70 | | 7 4 Return Sector Size | 71 | | 8 5 Return Volume Size | 72 | | 9 2 Media Changed | 73 | | 10 7 Audio Disk Info | 74 | | 11 7 Audio Track Info | 75 | | 12 11 Audio Q-Channel Info | 76 | | 13 13 Audio Sub-Channel Info | 77 | | 14 11 UPC Code | 78 | | 15 11 Audio Status Info | 79 | | 16-255 ? Reserved | 80 | +-----------------------------------------------------------------+ 81 | ^ 82 | 83 | IO_AUCHINF equ 4 84 | IO_DEVSTAT equ 6 85 | IO_VOLSIZE equ 8 86 | IO_DISKINFO equ 10 87 | IO_TRKINFO equ 11 88 | IO_QCHANNEL equ 12 89 | IO_UPC equ 14 90 | IO_AUSTAT equ 15 91 | 92 | ioctl04 struct ;audio channel info 93 | cmd db ? 94 | ich0 db ? ;input channel for output channel 0 95 | vol0 db ? ;volume for output channel 0 96 | ich1 db ? ;input channel for output channel 1 97 | vol1 db ? ;volume for output channel 1 98 | ich2 db ? ;input channel for output channel 2 99 | vol2 db ? ;volume for output channel 2 100 | ich3 db ? ;input channel for output channel 3 101 | vol3 db ? ;volume for output channel 3 102 | ioctl04 ends 103 | 104 | ioctl06 struct ;device status 105 | cmd db ? 106 | status dd ? 107 | ioctl06 ends 108 | 109 | ioctl08 struct ;get volume size 110 | cmd db ? 111 | sectors dd ? ;size in sectors 112 | ioctl08 ends 113 | 114 | ioctl10 struct ;audio disk info 115 | cmd db ? 116 | first db ? 117 | last db ? 118 | union 119 | leadout dd ? 120 | struct 121 | leadout_f db ? 122 | leadout_s db ? 123 | leadout_m db ? 124 | ends 125 | ends 126 | ioctl10 ends 127 | 128 | ioctl11 struct ;audio track info 129 | cmd db ? 130 | track db ? 131 | union 132 | start dd ? 133 | struct 134 | start_f db ? 135 | start_s db ? 136 | start_m db ? 137 | ends 138 | ends 139 | ctlinfo db ? 140 | ioctl11 ends 141 | 142 | ioctl12 struct ;audio q-channel info 143 | cmd DB ? 144 | ctladr DB ? ; CONTROL and ADR byte 145 | tno DB ? ; Track number (TNO) 146 | index DB ? ; (POINT) or Index (X) 147 | ; Running time within a track 148 | min DB ? ; (MIN) 149 | sec DB ? ; (SEC) 150 | frame DB ? ; (FRAME) 151 | DB ? ; (ZERO) 152 | ; Running time on the disk 153 | amin DB ? ; (AMIN) or (PMIN) 154 | asec DB ? ; (ASEC) or (PSEC) 155 | aframe DB ? ; (AFRAME) or (PFRAME) 156 | ioctl12 ends 157 | 158 | ioctl14 struct ;UPC code 159 | cmd DB ? 160 | ctladr DB ? ; CONTROL and ADR byte 161 | upcean DB 7 dup (?) ; UPC/EAN code 162 | ; last 4 bits are zero; the low-order nibble of byte 7 163 | DB ? ; Zero 164 | aframe DB ? ; Aframe 165 | ioctl14 ends 166 | 167 | ioctl15 struct ;audio status 168 | cmd DB ? 169 | status DW ? ; status bits 170 | union 171 | startl DD ? ; start for next resume 172 | struct 173 | startl_f DB ? 174 | startl_s DB ? 175 | startl_m DB ? 176 | ends 177 | ends 178 | union 179 | endl DD ? ; end for next resume 180 | struct 181 | endl_f DB ? 182 | endl_s DB ? 183 | endl_m DB ? 184 | ends 185 | ends 186 | ioctl15 ends 187 | 188 | cmd84 struct ;play audio 189 | reqhdr <> 190 | mode db ? ;addressing mode (see below) 191 | stasecs dd ? ;number of first sector to play 192 | numsecs dd ? ;sectors to play 193 | cmd84 ends 194 | 195 | HSG_MODE equ 00h 196 | RB_MODE equ 01h ;redbook mode 197 | 198 | cmd85 struct ;stop play audio 199 | reqhdr <> 200 | cmd85 ends 201 | 202 | .data 203 | 204 | drive dw 0 205 | bTrack db -1 206 | bOption db 0 207 | adrcnt dd 16 dup (0) ;counter for all Q-Channel ADR modes 208 | 209 | OPT_NOWAIT equ 1 ;just start playing, don't wait 210 | OPT_NOBUSY equ 2 ;don't check busy flag 211 | OPT_STOP equ 4 ;stop audio playing 212 | 213 | align word 214 | req03 cmd03 <<>,,buffer> 215 | req84 cmd84 <> 216 | req85 cmd85 <> 217 | 218 | .data? 219 | 220 | buffer db 130 dup (?) ;130=max for read ioctl 221 | org buffer 222 | ioc04 ioctl04 <> 223 | org buffer 224 | ioc06 ioctl06 <> 225 | org buffer 226 | ioc08 ioctl08 <> 227 | org buffer 228 | ioc10 ioctl10 <> 229 | org buffer 230 | ioc11 ioctl11 <> 231 | org buffer 232 | ioc12 ioctl12 <> 233 | org buffer 234 | ioc14 ioctl14 <> 235 | tmpstr db 14 dup (?) 236 | org buffer 237 | ioc15 ioctl15 <> 238 | 239 | .code 240 | 241 | include printf.inc 242 | 243 | ;--- convert redbook address in EAX to LBA 244 | 245 | CvtLBA proc 246 | mov cx,ax ;Save "seconds" & "frames" in CX-reg. 247 | shr eax,16 ;"minute" value to AX 248 | cmp ax,99 ;Is "minute" value too large? 249 | ja error 250 | cmp ch,60 ;Is "second" value too large? 251 | ja error 252 | cmp cl,75 ;Is "frame" value too large? 253 | ja error 254 | 255 | ;--- convert minute value to seconds 256 | mov edx,60 257 | mul dl 258 | mov dl,ch ;add "second" value. 259 | add ax,dx ;now ax contains seconds 260 | 261 | ;--- convert seconds to frames 262 | mov dl,75 263 | mul edx 264 | mov dl,150 ;add "frame" value by subtracting it from "2 sec" offset 265 | sub dl,cl 266 | sub eax,edx 267 | ret 268 | error: 269 | mov eax,100*60*75 ;error, set value to max (450.000) 270 | ret 271 | CvtLBA endp 272 | 273 | ;--- send device driver request (via MSCDEX) 274 | 275 | SendReq proc stdcall req:ptr BYTE 276 | 277 | mov bx,req 278 | push ds 279 | pop es 280 | mov cx,drive 281 | clc ;XP needs this 282 | mov ax,1510h ;send dev. req. 283 | int 2Fh 284 | ret 285 | 286 | SendReq endp 287 | 288 | ;--- get cmdline parameters 289 | ;--- ES=PSP 290 | 291 | getparam proc near 292 | mov bx,0080h 293 | nextchar: 294 | inc bx 295 | mov al,es:[bx] 296 | cmp al,cr 297 | jz done 298 | cmp al,0 299 | jz done 300 | cmp al,'/' 301 | jz @F 302 | cmp al,'-' 303 | jz @F 304 | cmp al,' ' 305 | jz nextchar 306 | cmp al,9 307 | jz nextchar 308 | call getnum 309 | jnc settno 310 | parerr: 311 | stc 312 | ret 313 | @@: 314 | inc bx 315 | mov al,es:[bx] 316 | or al,20h 317 | cmp al,'n' 318 | jz opt_n 319 | cmp al,'b' 320 | jz opt_b 321 | cmp al,'s' 322 | jz opt_s 323 | jmp parerr 324 | opt_n: 325 | or bOption, OPT_NOWAIT 326 | jmp nextchar 327 | opt_b: 328 | or bOption, OPT_NOBUSY 329 | jmp nextchar 330 | opt_s: 331 | or bOption, OPT_STOP 332 | jmp nextchar 333 | settno: 334 | mov bTrack, al 335 | dec bx 336 | jmp nextchar 337 | done: 338 | clc 339 | ret 340 | getnum: 341 | xor dx,dx 342 | nextdigit: 343 | mov al,es:[bx] 344 | cmp al,'0' 345 | jb nodigit 346 | cmp al,'9' 347 | ja nodigit 348 | sub al,'0' 349 | shl dx,1 350 | mov cx,dx 351 | shl dx,2 352 | add dx,cx 353 | mov ah,0 354 | add dx,ax 355 | inc bx 356 | jmp nextdigit 357 | nodigit: 358 | cmp al,' ' 359 | jz @F 360 | cmp al,9 361 | jz @F 362 | cmp al,cr 363 | jz @F 364 | stc 365 | @@: 366 | mov ax,dx 367 | ret 368 | getparam endp 369 | 370 | 371 | main proc c 372 | 373 | local startRB:dword ;start in redbook format 374 | local endRB:dword ;end in redbook format 375 | local numsecs:dword ;sectors to play 376 | local dwTimer:dword 377 | local starttrack:byte 378 | local endtrack:byte 379 | 380 | call getparam 381 | jnc @F 382 | invoke printf, CStr("usage: PLAYCD [ options ] ",lf,"options are:",lf) 383 | invoke printf, CStr(" -b: ignore busy flag (use ESC to stop!)",lf) 384 | invoke printf, CStr(" -n: don't wait for play audio command to finish",lf) 385 | invoke printf, CStr(" -s: stop playing audio",lf) 386 | invoke printf, CStr(" if isn't specified, all tracks are played",lf) 387 | jmp exit 388 | @@: 389 | ;--- call MSCDEX to see if there's a CD/DVD at all 390 | mov ax, 1500h 391 | mov bx, 0000 392 | int 2Fh 393 | cmp bx, 0000 394 | jnz @F 395 | invoke printf, CStr("no CD-ROM drive found",lf) 396 | jmp exit 397 | @@: 398 | mov drive, cx 399 | 400 | test bOption, OPT_STOP 401 | jnz stopplaying 402 | 403 | ;--- get audio channel info 404 | mov req03.len, sizeof cmd03 405 | mov req03.cmd, IOCREAD 406 | mov req03.numbyt, sizeof ioctl04 407 | mov ioc04.cmd, IO_AUCHINF ;get audio channel info 408 | invoke SendReq, addr req03 409 | jnc @F 410 | invoke printf, CStr("IOCTL audio channel info call failed [%X]",lf), ax 411 | jmp exit 412 | @@: 413 | invoke printf, CStr("IOCTL audio channel info [%X]: ch0=%02X/%02X ch1=%02X/%02X ch2=%02X/%02X ch3=%02X/%02X",lf), 414 | req03.status, ioc04.ich0, ioc04.vol0, ioc04.ich1, ioc04.vol1, ioc04.ich2, ioc04.vol2, ioc04.ich3, ioc04.vol3 415 | 416 | ;--- get device params 417 | mov req03.len, sizeof cmd03 418 | mov req03.cmd, IOCREAD ; IOCTL input 419 | mov req03.numbyt, sizeof ioctl06 420 | mov ioc06.cmd, IO_DEVSTAT ; get device status 421 | invoke SendReq, addr req03 422 | jnc @F 423 | invoke printf, CStr("IOCTL device status call failed [%X]",lf), ax 424 | jmp exit 425 | @@: 426 | invoke printf, CStr("IOCTL device status [%X]: params=%08lX",lf), req03.status, ioc06.status 427 | test ioc06.status, 1 428 | jz @F 429 | invoke printf, CStr("status[0]=1: door is open",lf) 430 | jmp exit 431 | @@: 432 | test ioc06.status, 10h 433 | jnz @F 434 | invoke printf, CStr("status[4]=0: no support to play audio - ignored!",lf) 435 | ;--- don't exit, try to play anyway! 436 | ; jmp exit 437 | @@: 438 | test req03.status, 200h ;busy? 439 | jz @F 440 | invoke printf, CStr("status[9]=1: device busy - will try to stop playing",lf) 441 | ;--- stop audio playing 442 | stopplaying: 443 | mov req85.len, sizeof cmd84 444 | mov req85.subunit, 0 445 | mov req85.cmd, STOP 446 | invoke SendReq, addr req85 447 | invoke printf, CStr("Stop Audio [%X]",lf), req85.status 448 | jmp exit 449 | @@: 450 | 451 | ;--- get volume size 452 | mov req03.len, sizeof cmd03 453 | mov req03.cmd, IOCREAD 454 | mov req03.numbyt, sizeof ioctl08 455 | mov ioc08.cmd, IO_VOLSIZE ;get volume size 456 | invoke SendReq, addr req03 457 | jnc @F 458 | invoke printf, CStr("IOCTL volume size call failed [%X]",lf), ax 459 | jmp novolsize 460 | @@: 461 | invoke printf, CStr("IOCTL volume size [%X]: sectors=%lu",lf), req03.status, ioc08.sectors 462 | novolsize: 463 | 464 | ;--- get disk info, set endRB, starttrack and endtrack values 465 | mov req03.len, sizeof cmd03 466 | mov req03.cmd, IOCREAD 467 | mov req03.numbyt, sizeof ioctl10 468 | mov ioc10.cmd, IO_DISKINFO ;get disk info 469 | invoke SendReq, addr req03 470 | jnc @F 471 | invoke printf, CStr("IOCTL disk info call failed [%X]",lf), ax 472 | jmp exit 473 | @@: 474 | invoke printf, CStr("IOCTL disk info [%X]: tracks %u-%u, leadout=%02u:%02u:%02u",lf), req03.status, ioc10.first, ioc10.last, ioc10.leadout_m, ioc10.leadout_s, ioc10.leadout_f 475 | mov eax, ioc10.leadout 476 | mov endRB, eax 477 | test req03.status, 8000h 478 | jnz exit 479 | mov al, bTrack 480 | cmp al, -1 481 | jnz @F 482 | mov al, ioc10.first 483 | @@: 484 | mov starttrack, al 485 | mov al, ioc10.last 486 | mov endtrack, al 487 | 488 | ;--- get UPC disk info 489 | mov req03.len, sizeof cmd03 490 | mov req03.cmd, IOCREAD 491 | mov req03.numbyt, sizeof ioctl14 492 | mov ioc14.cmd, IO_UPC ;get UPC code 493 | invoke SendReq, addr req03 494 | jnc @F 495 | invoke printf, CStr("IOCTL UPC call failed [%X]",lf), ax 496 | jmp noupc 497 | @@: 498 | test req03.status, 8000h 499 | jz @F 500 | invoke printf, CStr("IOCTL UPC code [%X]",lf), req03.status 501 | jmp noupc 502 | @@: 503 | ;--- convert UPC BCD string to ASCII 504 | lea si, ioc14.upcean 505 | lea di, tmpstr 506 | push ds 507 | pop es 508 | mov cx, 7 509 | nextbyte: 510 | lodsb 511 | db 0d4h, 10h ;aam 10h 512 | add ax, 3030h 513 | xchg al, ah 514 | stosw 515 | loop nextbyte 516 | mov [di-1], cl ;terminate ASCII with 0 517 | invoke printf, CStr("IOCTL UPC code [%X]: CA=%02X UPC=%s",lf), req03.status, ioc14.ctladr, addr tmpstr 518 | noupc: 519 | 520 | ;--- get audio status info 521 | mov req03.len, sizeof cmd03 522 | mov req03.cmd, IOCREAD 523 | mov req03.numbyt, sizeof ioctl15 524 | mov ioc15.cmd, IO_AUSTAT ;get audio status 525 | invoke SendReq, addr req03 526 | jnc @F 527 | invoke printf, CStr("IOCTL audio status call failed [%X]",lf), ax 528 | jmp noaustat 529 | @@: 530 | test req03.status, 8000h 531 | jz @F 532 | invoke printf, CStr("IOCTL audio status [%X]",lf), req03.status 533 | jmp noaustat 534 | @@: 535 | invoke printf, CStr("IOCTL audio status [%X]: status=%04X start=%02u:%02u:%02u end=%02u:%02u:%02u",lf), 536 | req03.status, ioc15.status, 537 | ioc15.startl_m, ioc15.startl_s, ioc15.startl_f, 538 | ioc15.endl_m, ioc15.endl_s, ioc15.endl_f 539 | noaustat: 540 | 541 | ;--- get info for first track to be played, set startRB value 542 | mov req03.len, sizeof cmd03 543 | mov req03.cmd, IOCREAD 544 | mov req03.numbyt, sizeof ioctl11 545 | mov ioc11.cmd, IO_TRKINFO ;get track info 546 | mov al, starttrack 547 | mov ioc11.track, al 548 | invoke SendReq, addr req03 549 | jnc @F 550 | invoke printf, CStr("IOCTL track %u info call failed [%X]",lf), starttrack, ax 551 | jmp exit 552 | @@: 553 | invoke printf, CStr("IOCTL track %u info [%X]: start=%02u:%02u:%02u, ctlinfo=%X",lf), starttrack, req03.status, ioc11.start_m, ioc11.start_s, ioc11.start_f, ioc11.ctlinfo 554 | test req03.status, 8000h 555 | jnz exit 556 | ;--- usually a cd starts at 00:02:00 (redbook), but we play it safe 557 | mov eax, ioc11.start 558 | mov startRB, eax 559 | mov al, ioc11.ctlinfo 560 | test al, 40h ;data track? 561 | jz @F 562 | invoke printf, CStr("track %u is a data track",lf), ioc11.track 563 | jmp exit 564 | @@: 565 | ;--- is track the last one? 566 | mov al, starttrack 567 | cmp al, endtrack 568 | jnc islasttrack 569 | ;--- or is the whole disk to be played? 570 | cmp bTrack, -1 571 | jz islasttrack 572 | ;--- then we don't need start of next track 573 | 574 | ;--- get info for next track, modify endRB value 575 | mov req03.len, sizeof cmd03 576 | mov req03.cmd, IOCREAD 577 | mov req03.numbyt, sizeof ioctl11 578 | mov ioc11.cmd, IO_TRKINFO ;get track info 579 | mov al, starttrack 580 | inc al 581 | mov ioc11.track, al 582 | invoke SendReq, addr req03 583 | jnc @F 584 | invoke printf, CStr("IOCTL track %u info call failed [%X]",lf), ioc11.track, ax 585 | jmp exit 586 | @@: 587 | invoke printf, CStr("IOCTL track %u info [%X]: start=%02u:%02u:%02u, ctlinfo=%X",lf), ioc11.track, req03.status, ioc11.start_m, ioc11.start_s, ioc11.start_f, ioc11.ctlinfo 588 | test req03.status, 8000h 589 | jnz exit 590 | mov eax,ioc11.start 591 | mov endRB, eax 592 | 593 | islasttrack: 594 | mov eax, startRB 595 | call CvtLBA 596 | mov esi, eax 597 | mov eax, endRB 598 | call CvtLBA 599 | sub eax, esi 600 | mov numsecs, eax 601 | invoke printf, CStr("start sector=%lu, num sectors=%lu",lf), esi, eax 602 | 603 | ;--- play track(s) 604 | mov req84.len, sizeof cmd84 605 | mov req84.subunit, 0 606 | mov req84.cmd, PLAY 607 | mov req84.mode, RB_MODE 608 | mov eax, startRB 609 | mov req84.stasecs, eax 610 | mov eax, numsecs 611 | mov req84.numsecs, eax 612 | invoke SendReq, addr req84 613 | jnc @F 614 | invoke printf, CStr("Play Audio failed [%X]",lf), req84.status 615 | jmp exit 616 | @@: 617 | invoke printf, CStr("Play Audio [%X]",lf), req84.status 618 | 619 | test bOption, OPT_NOWAIT 620 | jnz exit 621 | 622 | ;--- wait until playing has terminated 623 | 624 | nextloop: 625 | call gettick 626 | mov dwTimer, eax 627 | ;--- read and display q-channel info in a loop 628 | mov req03.len, sizeof cmd03 629 | mov req03.cmd, IOCREAD 630 | mov req03.numbyt, sizeof ioctl12 631 | if 1 ;clear the buffer (XP returns success, but won't supply any values) 632 | xor eax, eax 633 | mov dword ptr ioc12+0, eax 634 | mov dword ptr ioc12+4, eax 635 | mov dword ptr ioc12+7, eax 636 | endif 637 | mov ioc12.cmd, IO_QCHANNEL ;get q-channel info 638 | invoke SendReq, addr req03 639 | jnc @F 640 | invoke printf, CStr("IOCTL Q-channel info call failed",lf) 641 | jmp exit 642 | @@: 643 | ;--- the TNO field is displayed in hex, because it's supposed to be BCD 644 | invoke printf, CStr("IOCTL Q-channel info [%X]: CA=%02X TNO=%02X X=%02X track=%02u:%02u:%02u disk=%02u:%02u:%02u",cr), 645 | req03.status, ioc12.ctladr, ioc12.tno, ioc12.index, ioc12.min, ioc12.sec, ioc12.frame, ioc12.amin, ioc12.asec, ioc12.aframe 646 | ;--- increase counter, use ADR value as index 647 | movzx eax, ioc12.ctladr 648 | and al, 0Fh 649 | inc adrcnt[eax*4] 650 | 651 | dowait: 652 | ;--- is a key pressed? 653 | mov ah, 01h 654 | int 16h 655 | jz @F 656 | mov ah,00 657 | int 16h 658 | cmp ah,01 659 | jnz @F 660 | invoke printf, CStr(lf) 661 | jmp stopplaying 662 | @@: 663 | call gettick 664 | sub eax, dwTimer 665 | cmp eax, 5 666 | jb dowait 667 | test bOption, OPT_NOBUSY 668 | jnz nextloop 669 | test req03.status, 0200h 670 | jnz nextloop 671 | 672 | exit: 673 | invoke printf, CStr(lf) 674 | ;--- display Q-channel info counters 675 | mov si, offset adrcnt 676 | xor di, di 677 | nextcnt: 678 | lodsd 679 | and eax, eax 680 | jz @F 681 | invoke printf, CStr("Q-channel info, mode %u: read %lu times",lf), di, eax 682 | @@: 683 | inc di 684 | cmp di, 16 685 | jb nextcnt 686 | ret 687 | gettick: 688 | push ds 689 | push 0 690 | pop ds 691 | mov eax, ds:[46ch] 692 | pop ds 693 | retn 694 | 695 | main endp 696 | 697 | ;--- init code. 698 | ;--- to set the "small" memory model, register SS must equal DS (=DGROUP) 699 | 700 | start: 701 | mov ax, @data 702 | mov ds, ax 703 | mov bx, ss 704 | sub bx, ax 705 | shl bx, 4 706 | mov ss, ax 707 | add sp, bx 708 | call main 709 | mov ax,4c00h 710 | int 21h 711 | 712 | .stack 800h 713 | 714 | END start 715 | -------------------------------------------------------------------------------- /PRINTF.INC: -------------------------------------------------------------------------------- 1 | 2 | ;--- simple printf() implementation 3 | 4 | ;--- ltob(long n, char * s, int base); 5 | ;--- convert long to string 6 | ;--- outb is expected to be onto stack 7 | 8 | ltob PROC stdcall uses edi number:dword, outb:word, base:word 9 | 10 | mov ch, 0 11 | movzx edi, base 12 | mov eax, number 13 | cmp di, -10 14 | jne @F 15 | mov di, 10 16 | and eax, eax 17 | jns @F 18 | neg eax 19 | mov ch, '-' 20 | @@: 21 | mov bx,outb 22 | add bx, 10 23 | mov BYTE PTR ss:[bx], 0 24 | dec bx 25 | @@nextdigit: 26 | xor edx, edx 27 | div edi 28 | add dl, '0' 29 | cmp dl, '9' 30 | jbe @F 31 | add dl, 7+20h 32 | @@: 33 | mov ss:[bx], dl 34 | dec bx 35 | and eax, eax 36 | jne @@nextdigit 37 | cmp ch,0 38 | je @F 39 | mov ss:[bx], ch 40 | dec bx 41 | @@: 42 | inc bx 43 | mov ax, bx 44 | ret 45 | 46 | ltob ENDP 47 | 48 | ;--- assumed ds=dgroup, ss=nothing, es:nothing 49 | 50 | printf PROC c uses si di bx fmt:ptr byte, args:VARARG 51 | 52 | local size_:word 53 | local flag:byte 54 | local longarg:byte 55 | local fill:byte 56 | local szTmp[12]:byte 57 | 58 | lea di, [fmt+2] 59 | contscan: 60 | mov si, [fmt] 61 | nextchar: 62 | lodsb 63 | or al, al 64 | je done 65 | cmp al, '%' 66 | je formatitem 67 | call handle_char 68 | jmp nextchar 69 | done: 70 | xor ax, ax 71 | ret 72 | 73 | formatitem: 74 | push contscan 75 | xor dx,dx 76 | mov [longarg], dl 77 | mov bl, 1 78 | mov cl,' ' 79 | cmp BYTE PTR [si], '-' 80 | jne @F 81 | dec bx 82 | inc si 83 | @@: 84 | mov [flag], bl 85 | cmp BYTE PTR [si], '0' 86 | jne @F 87 | mov cl, '0' 88 | inc si 89 | @@: 90 | mov [fill], cl 91 | mov bx, dx 92 | 93 | .while byte ptr [si] >= '0' && byte ptr [si] <= '9' 94 | lodsb 95 | sub al, '0' 96 | cbw 97 | imul cx, bx, 10 ;cx = bx * 10 98 | add ax, cx 99 | mov bx, ax 100 | .endw 101 | 102 | mov [size_], bx 103 | cmp BYTE PTR [si], 'l' 104 | jne @F 105 | mov [longarg], 1 106 | inc si 107 | @@: 108 | lodsb 109 | mov [fmt], si 110 | cmp al, 'x' 111 | je handle_x 112 | cmp al, 'X' 113 | je handle_x 114 | cmp al, 'c' 115 | je handle_c 116 | cmp al, 'd' 117 | je handle_d 118 | cmp al, 'i' 119 | je handle_i 120 | cmp al, 's' 121 | je handle_s 122 | cmp al, 'u' 123 | je handle_u 124 | cmp al, 0 125 | jnz @@L359 126 | pop ax 127 | jmp done 128 | handle_c: 129 | mov ax, ss:[di] 130 | add di, 2 131 | @@L359: 132 | call handle_char 133 | retn 134 | 135 | handle_x: 136 | mov bx, 16 137 | jmp @@lprt262 138 | handle_d: 139 | handle_i: 140 | mov bx, -10 141 | jmp @@lprt262 142 | handle_u: 143 | mov bx, 10 144 | @@lprt262: 145 | mov ax, ss:[di] 146 | add di, 2 147 | sub dx, dx 148 | cmp bx, dx ;signed or unsigned? 149 | jge @F 150 | cwd 151 | @@: 152 | cmp [longarg], 0 153 | je @F 154 | mov dx, ss:[di] 155 | add di, 2 156 | @@: 157 | lea cx, [szTmp] 158 | invoke ltob, dx::ax, cx, bx 159 | mov si, ax 160 | push ds 161 | push ss 162 | pop ds 163 | call output_string 164 | pop ds 165 | retn 166 | 167 | handle_s: 168 | mov si, ss:[di] 169 | add di, 2 170 | 171 | output_string: ;display string at ds:si 172 | mov ax, si 173 | mov bx, size_ 174 | .while byte ptr [si] 175 | inc si 176 | .endw 177 | sub si, ax 178 | xchg ax, si 179 | sub bx, ax 180 | .if flag == 1 181 | .while sword ptr bx > 0 182 | mov al, [fill] 183 | call handle_char 184 | dec bx 185 | .endw 186 | .endif 187 | 188 | .while byte ptr [si] 189 | lodsb 190 | call handle_char 191 | .endw 192 | 193 | .while sword ptr bx > 0 194 | mov al, [fill] 195 | call handle_char 196 | dec bx 197 | .endw 198 | retn 199 | 200 | handle_char: 201 | 202 | mov dl, al 203 | cmp al, 10 204 | jnz @F 205 | mov dl, 13 206 | call @F 207 | mov dl, 10 208 | @@: 209 | mov ah, 2 210 | int 21h 211 | retn 212 | 213 | printf ENDP 214 | 215 | -------------------------------------------------------------------------------- /make.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | jwasm -mz -nologo -Fl=build\PlayCD -Fo=build\PlayCD PlayCD.asm 3 | --------------------------------------------------------------------------------