- Timestamp:
- Jul 17, 2000, 8:37:33 PM (25 years ago)
- Location:
- sbliveos2/trunk
- Files:
-
- 1 added
- 15 edited
Legend:
- Unmodified
- Added
- Removed
-
sbliveos2/trunk/ChangeLog
r151 r152 1 1 $Id$ 2 3 2000-07-13: Sander van Leeuwen <sandervl@xs4all.nl> 4 - Added different levels of debug logging (dprintf2/3/4) 5 6 2000-06-19: Sander van Leeuwen <sandervl@xs4all.nl> 7 - Added configuration output in 16 bits driver 8 - Check for 0 size in memcopy Linux apis (fixed trap d on some machines) 9 - Added extra checks for null pointers, fixed null pointer check 10 in drv16\ossidc16.cpp (IDC16_MALLOC) 2 11 3 12 2000-05-20: Sander van Leeuwen <sandervl@xs4all.nl> -
sbliveos2/trunk/drv16/commdbg.c
r142 r152 39 39 #include <os2.h> 40 40 #include "dbgos2.h" 41 #include "commdbg.h" 41 42 42 43 BOOL fLineTerminate=TRUE; 43 #ifdef DEBUG44 44 45 45 #define CR 0x0d … … 50 50 #define SIGNIFICANT_FIELD 0x0007 51 51 52 int dbglevel = 2; 52 53 53 54 char hextab[]="0123456789ABCDEF"; 54 55 55 56 //-------------------- DecWordToASCII - 56 char far * DecWordToASCII(char far *StrPtr, USHORT wDecVal, USHORT Option)57 char far * cdecl DecWordToASCII(char far *StrPtr, USHORT wDecVal, USHORT Option) 57 58 { 58 59 BOOL fNonZero=FALSE; … … 97 98 98 99 //-------------------- DecLongToASCII - 99 char far * DecLongToASCII(char far *StrPtr, ULONG lDecVal,USHORT Option)100 char far * cdecl DecLongToASCII(char far *StrPtr, ULONG lDecVal,USHORT Option) 100 101 { 101 102 BOOL fNonZero=FALSE; … … 148 149 } 149 150 //-------------------- HexWordToASCII - 150 char far * HexWordToASCII(char far *StrPtr, USHORT wHexVal, USHORT Option)151 char far * cdecl HexWordToASCII(char far *StrPtr, USHORT wHexVal, USHORT Option) 151 152 { 152 153 BOOL fNonZero=FALSE; … … 176 177 177 178 //-------------------- HexLongToASCII - 178 char far * HexLongToASCII(char far *StrPtr, ULONG wHexVal, USHORT Option)179 char far * cdecl HexLongToASCII(char far *StrPtr, ULONG wHexVal, USHORT Option) 179 180 { 180 181 BOOL fNonZero=FALSE; … … 217 218 } 218 219 220 #ifdef DEBUG 219 221 char BuildString[1024]; 220 #endif // DEBUG221 #ifdef DEBUG222 222 223 223 //------------------------- PrintfOut - -
sbliveos2/trunk/drv16/init.cpp
r151 r152 55 55 #include <include.h> // Pragmas and more. 56 56 #include <sbversion.h> 57 #include "commdbg.h" 58 #include <dbgos2.h> 57 59 58 60 #ifndef PCI_VENDOR_ID_CREATIVE … … 72 74 static char NEWLINE[] = "\r\n"; 73 75 static char szSBLiveNotFound[] = "SB Live! hardware not detected!"; 74 static char szAltF1[] = "Reboot, press Alt-F1 during OS/2 startup and select Enable Hardware Detection"; 76 static char szSBLiveConfig1[] = "SB Live! configuration: IRQ "; 77 static char szSBLiveConfig2[] = ", IO Port 0x"; 78 75 79 76 80 // … … 78 82 // Name is copied from header at runtime. 79 83 // 80 char szPddName [9] = {0}; 84 char szPddName[9] = {0}; 85 char digit[16] = {0}; 81 86 82 87 ResourceManager* pRM = 0; // Resource manager object. … … 122 127 123 128 DosWrite(1, (VOID FAR*)NEWLINE, sizeof(NEWLINE)-1, &result); 124 DosWrite(1, (VOID FAR*)szSBLive, s trlen(szSBLive), &result);129 DosWrite(1, (VOID FAR*)szSBLive, sizeof(szSBLive)-1, &result); 125 130 DosWrite(1, (VOID FAR*)NEWLINE, sizeof(NEWLINE)-1, &result); 126 131 … … 137 142 return; 138 143 } 139 else if (pRM->getState() != rmDriverCreated) {140 return;141 }142 144 143 145 //SvL: Check if SB Live hardware has been detected by the resource manager 144 // If not, tell user to reboot, press alt-f1 and enable hardware detection 145 if(!pRM->bIsDevDetected(PCI_ID, SEARCH_ID_DEVICEID, TRUE)) { 146 if(pRM->getState() != rmDriverCreated || !pRM->bIsDevDetected(PCI_ID, SEARCH_ID_DEVICEID, TRUE)) 147 { 148 hardware_notfound: 146 149 USHORT result; 147 150 148 DosWrite(1, (VOID FAR*)szSBLiveNotFound, strlen(szSBLiveNotFound), &result); 149 DosWrite(1, (VOID FAR*)NEWLINE, sizeof(NEWLINE)-1, &result); 150 DosWrite(1, (VOID FAR*)szAltF1, sizeof(szAltF1), &result); 151 DosWrite(1, (VOID FAR*)szSBLiveNotFound, sizeof(szSBLiveNotFound)-1, &result); 152 DosWrite(1, (VOID FAR*)NEWLINE, sizeof(NEWLINE)-1, &result); 151 153 DosWrite(1, (VOID FAR*)NEWLINE, sizeof(NEWLINE)-1, &result); 152 154 return; 153 155 } 156 LDev_Resources *pResources = pRM->pGetDevResources(PCI_ID, SEARCH_ID_DEVICEID, TRUE); 157 if ((!pResources) || pResources->isEmpty()) { 158 goto hardware_notfound; 159 } 160 161 if (fVerbose) { 162 USHORT result; 163 164 DecWordToASCII(digit, (USHORT)pResources->uIRQLevel[0], 0); 165 DosWrite(1, (VOID FAR*)szSBLiveConfig1, sizeof(szSBLiveConfig1)-1, &result); 166 DosWrite(1, (VOID FAR*)digit, strlen(digit), &result); 167 168 DosWrite(1, (VOID FAR*)szSBLiveConfig2, sizeof(szSBLiveConfig2)-1, &result); 169 HexWordToASCII(digit, pResources->uIOBase[0], 0); 170 DosWrite(1, (VOID FAR*)digit, strlen(digit), &result); 171 172 DosWrite(1, (VOID FAR*)NEWLINE, sizeof(NEWLINE)-1, &result); 173 DosWrite(1, (VOID FAR*)NEWLINE, sizeof(NEWLINE)-1, &result); 174 } 175 delete pResources; 154 176 155 177 // Build the MPU401 object only if we got a good 2115 init. -
sbliveos2/trunk/drv16/irq.cpp
r142 r152 270 270 cli(); 271 271 if (_handlerList[i].pfnHandler(_usIRQLevel)) { // Call handler. 272 cli(); 272 273 // We've cleared all service requests. Send EOI and clear 273 274 // the carry flag (tells OS/2 kernel that Int was handled). -
sbliveos2/trunk/drv16/ossidc16.cpp
r151 r152 182 182 //****************************************************************************** 183 183 //****************************************************************************** 184 ULONG OSS16_StreamGetSpace(STREAM *stream) 185 { 186 return (BOOL)CallOSS32(IDC32_STREAM_GETSPACE, stream->ulSysFileNum, MMPMToOSSStreamType(stream->ulStreamType), stream->ulStreamId, 0, 0); 187 } 188 //****************************************************************************** 189 //****************************************************************************** 184 190 ULONG CallOSS32(USHORT cmd, ULONG fileid, ULONG param1, ULONG param2, ULONG param3, ULONG param4) 185 191 { … … 292 298 idcres->memlength[i] = pResources->uMemLength[i]; 293 299 } 294 300 delete pResources; 295 301 return 1; 296 302 } … … 298 304 { 299 305 LIN linaddr; 300 ULONG far *addr = (ULONG far *)malloc((USHORT)packet->malloc.size+4); 301 302 if(addr == NULL) { 306 ULONG near *addr16 = (ULONG near *)malloc((USHORT)packet->malloc.size+4); 307 ULONG far *addr = (ULONG far *)addr16; 308 309 if(addr16 == NULL) { 303 310 return 0; 304 311 } -
sbliveos2/trunk/drv16/parse.c
r142 r152 189 189 190 190 switch (cParm) { 191 case '3': 192 fInt3BeforeInit = TRUE; 193 break; 191 194 case 'J': // which MIDI. MPU by default, if /J then FMSYNTH 192 195 fFMforMIDI = TRUE; -
sbliveos2/trunk/drv16/rm.cpp
r151 r152 167 167 #if 1 168 168 //Manual detection in ResourceManager class constructor; 169 return _state == rmDriverCreated;169 return (_state == rmDriverCreated || _state == rmAdapterCreated); 170 170 #else 171 171 BOOL bReturn = FALSE; … … 375 375 if (_state != rmAdapterCreated) { 376 376 rc = _rmCreateAdapter(); 377 } 378 379 // Register the device with OS/2 RM.380 _rmCreateDevice((unsigned char __far *)DeviceName, pahResources );377 378 // Register the device with OS/2 RM. 379 _rmCreateDevice((unsigned char __far *)DeviceName, pahResources ); 380 } 381 381 382 382 exit: -
sbliveos2/trunk/drv16/wavestrm.cpp
r151 r152 34 34 #include "ioctl.h" 35 35 36 #ifndef min 37 #define min(a,b) (a>b) ? b : a 38 #endif 39 36 40 // 37 41 // _vRealignBuffer … … 152 156 void WAVESTREAM::AddBuffers(void) 153 157 { 158 ULONG space, byteswritten; 159 154 160 if (ulStreamType & STREAM_WRITE) { 155 161 if(!qhInProcess.Head() && !qhDone.Head()) { … … 160 166 return; 161 167 } 162 AddBuffer(); 163 AddBuffer(); 168 space = OSS16_StreamGetSpace(this); 169 if(space > 128) { 170 space -= 128; 171 } 172 while(space) { 173 byteswritten = AddBuffer(space); 174 space -= byteswritten; 175 if(byteswritten == 0) break; 176 } 164 177 } 165 178 } … … 171 184 // there are buffers on pHead... BEWARE 172 185 // 173 void WAVESTREAM::AddBuffer()186 ULONG WAVESTREAM::AddBuffer(ULONG space) 174 187 { 175 188 PSTREAMBUFFER pTemp = (PSTREAMBUFFER)qhDone.Head(); … … 180 193 pTemp = (PSTREAMBUFFER)qhInProcess.Head(); 181 194 } 182 if(!pTemp) return ;195 if(!pTemp) return 0; 183 196 184 197 // get the buffer pointer and amount of data remaining … … 187 200 188 201 // write the audio buffer 202 Buff_left = min(Buff_left, space); 189 203 byteswritten = OSS16_StreamAddBuffer(this, pdataBuf, Buff_left); 190 204 if(byteswritten == 0) { 191 return; //no more room 192 } 193 194 // dprintf(("AddBuffer %lx size %d, bytes written %d", pdataBuf, (USHORT)Buff_left, (USHORT)byteswritten)); 205 return 0; //no more room 206 } 195 207 196 208 // update the buffer pos counter … … 200 212 qhDone.PushOnTail(qhInProcess.PopHead()); 201 213 } 214 dprintf4(("AddBuffer %lx size %d, bytes written %d", pdataBuf, (USHORT)Buff_left, (USHORT)byteswritten)); 215 return byteswritten; 202 216 } 203 217 … … 226 240 } 227 241 228 //// dprintf(("_vReadAudioBuf %lx size %d, bytes read %d", pdataBuf, Buff_left, bytesread));242 dprintf4(("_vReadAudioBuf %lx size %d, bytes read %d", pdataBuf, Buff_left, bytesread)); 229 243 230 244 // update the buffer pos counter … … 236 250 } 237 251 if(pTemp->ulBuffpos == pTemp->ulBuffsz) { 238 dprintf (("_vReadAudioBuf return buffer %lx size %ld, bytes read %ld", (ULONG)pTemp->pBuffptr, pTemp->ulBuffsz, bytesread));252 dprintf4(("_vReadAudioBuf return buffer %lx size %ld, bytes read %ld", (ULONG)pTemp->pBuffptr, pTemp->ulBuffsz, bytesread)); 239 253 ReturnBuffer(); 240 254 } … … 280 294 //calc position in next buffer 281 295 bytesinc = ptemp->ulDonepos - ptemp->ulBuffsz; 282 //// dprintf(("Process: Return buffer %lx size %d", ptemp->pBuffptr, ptemp->ulBuffsz));296 dprintf3(("Process: Return buffer %lx size %d", ptemp->pBuffptr, ptemp->ulBuffsz)); 283 297 ReturnBuffer(); 284 298 } … … 309 323 { 310 324 qhInProcess.PushOnTail((PQUEUEELEMENT)new STREAMBUFFER(uLength, pbuf)); 311 //// dprintf(("WAVESTREAM::Write: Push on tail %lx %d", ((PSTREAMBUFFER)qhInProcess.Head())->pBuffptr, ((PSTREAMBUFFER)qhInProcess.Head())->ulBuffsz));325 dprintf2(("WAVESTREAM::Write: Push on tail %lx %d", ((PSTREAMBUFFER)qhInProcess.Head())->pBuffptr, ((PSTREAMBUFFER)qhInProcess.Head())->ulBuffsz)); 312 326 if(fUnderrun) { 313 327 fUnderrun = FALSE; … … 323 337 { 324 338 qhInProcess.PushOnTail((PQUEUEELEMENT)new STREAMBUFFER(uLength, pbuf)); 325 dprintf (("WAVESTREAM::Read: Push on tail %lx %d", ((PSTREAMBUFFER)qhInProcess.Head())->pBuffptr, ((PSTREAMBUFFER)qhInProcess.Head())->ulBuffsz));339 dprintf2(("WAVESTREAM::Read: Push on tail %lx %d", ((PSTREAMBUFFER)qhInProcess.Head())->pBuffptr, ((PSTREAMBUFFER)qhInProcess.Head())->ulBuffsz)); 326 340 return 0; 327 341 } … … 384 398 ULONG fragsize; 385 399 400 #ifdef DEBUG 401 int oldlevel = dbglevel; 402 dbglevel = 4; 403 #endif 386 404 // configure the wave device 387 405 ((PWAVEAUDIO)pahw)->ConfigDev(this, &_configinfo); … … 421 439 OSS16_StartStream(this); 422 440 } 441 #ifdef DEBUG 442 dbglevel = oldlevel; 443 #endif 423 444 424 445 //Must set volume after adding buffers (voices inside sblive driver might not -
sbliveos2/trunk/drv16/wavestrm.hpp
r142 r152 62 62 void _vRealignPausedBuffers(ULONG endpos = 0); 63 63 void AddBuffers(); // Initialize the audio buffer object 64 void AddBuffer(); // write one buffer to the audio buffer64 ULONG AddBuffer(ULONG space); // write one buffer to the audio buffer 65 65 BOOL _vReadAudioBuf(void); // read data from the audio buffer 66 66 -
sbliveos2/trunk/drv32/idc.c
r151 r152 113 113 case IDC32_STREAM_RESET: 114 114 return OSS32_StreamReset(pPacket->startstop.streamtype, pPacket->startstop.streamid); 115 116 case IDC32_STREAM_GETSPACE: 117 return OSS32_StreamGetSpace(pPacket->getspace.streamtype, pPacket->getspace.streamid); 115 118 116 119 case IDC32_STREAM_IOCTL: -
sbliveos2/trunk/drv32/makefile.os2
r148 r152 45 45 CFLAGS = $(CFLAGS) -mf -DKEE 46 46 ASFLAGS = $(ASFLAGS) -D:KEE 47 LNKFILE = sblivekee.lnk 47 48 !else 48 49 CFLAGS = $(CFLAGS) -mc -zu 50 LNKFILE = sblive.lnk 49 51 !endif 50 52 … … 113 115 all: $(TARGET).sys 114 116 115 $( TARGET).lnk: makefile.os2 ..\include\version.mak117 $(LNKFILE): makefile.os2 ..\include\version.mak 116 118 @%write $^@ name $(TARGET).sys 117 119 @%write $^@ option alignment=16 … … 124 126 @%write $^@ library $(%WATCOM)\lib386\os2\clib3r.lib 125 127 126 $(TARGET).sys: $( TARGET).lnk$(FILES)127 $(LINK) @$( TARGET).lnk128 $(TARGET).sys: $(LNKFILE) $(FILES) 129 $(LINK) @$(LNKFILE) 128 130 ..\drv16\wat2map $(WMAPNAME) $(TARGET).MAP 129 131 mapsym $(TARGET).MAP -
sbliveos2/trunk/lib32/memory.cpp
r148 r152 129 129 void __copy_user(void *to, const void *from, unsigned long n) 130 130 { 131 if(to == NULL || from == NULL) { 132 DebugInt3(); 133 return; 134 } 135 if(n == 0) return; 131 136 #ifdef KEE 132 137 memcpy(to, from, n); … … 139 144 unsigned long copy_to_user(void *to, const void *from, unsigned long n) 140 145 { 146 if(to == NULL || from == NULL) { 147 DebugInt3(); 148 return 0; 149 } 150 if(n == 0) return 0; 141 151 #ifdef KEE 142 152 memcpy(to, from, n); … … 150 160 void __copy_user_zeroing(void *to, const void *from, unsigned long n) 151 161 { 162 if(to == NULL || from == NULL) { 163 DebugInt3(); 164 return; 165 } 166 if(n == 0) return; 152 167 copy_to_user(to, from, n); 153 168 } … … 156 171 unsigned long copy_from_user(void *to, const void *from, unsigned long n) 157 172 { 173 if(to == NULL || from == NULL) { 174 DebugInt3(); 175 return 0; 176 } 177 if(n == 0) return 0; 158 178 #ifdef KEE 159 179 memcpy(to, from, n); … … 167 187 int get_user(int size, void *dest, void *src) 168 188 { 189 if(size == 0) return 0; 190 191 if(dest == NULL || src == NULL) { 192 DebugInt3(); 193 return 0; 194 } 169 195 #ifdef KEE 170 196 memcpy(dest, src, size); … … 178 204 int put_user(int x, void *ptr) 179 205 { 206 if(ptr == NULL) { 207 DebugInt3(); 208 return 0; 209 } 210 180 211 *(int *)ptr = x; 181 212 return 0; … … 200 231 char near *addr; 201 232 233 if(size == 0) { 234 DebugInt3(); 235 return NULL; 236 } 202 237 if(size > 1024) { 203 238 #ifdef KEE -
sbliveos2/trunk/lib32/sound.c
r151 r152 451 451 //****************************************************************************** 452 452 //****************************************************************************** 453 ULONG OSS32_StreamGetSpace(ULONG streamtype, ULONG streamid) 454 { 455 struct inode ossinode; 456 struct file ossfile; 457 struct dentry ossdentry; 458 ULONG ossid = streamtype & OSS_IDMASK; 459 audio_buf_info info; 460 char NEAR *tmpbuf; 461 ULONG cmd; 462 int transferred; 463 464 ossinode.i_rdev = ossid; 465 ossfile.private_data = (void *)streamid; 466 ossfile.f_flags = 0; 467 ossfile.f_dentry = &ossdentry; 468 ossdentry.d_inode = &ossinode; 469 470 switch(streamtype) { 471 case OSS_STREAM_WAVEOUT: 472 ossfile.f_mode = FMODE_WRITE; 473 cmd = SNDCTL_DSP_GETOSPACE; 474 break; 475 case OSS_STREAM_WAVEIN: 476 ossfile.f_mode = FMODE_READ; 477 cmd = SNDCTL_DSP_GETISPACE; 478 break; 479 case OSS_STREAM_MIDIOUT: 480 ossfile.f_mode = FMODE_WRITE; 481 break; 482 case OSS_STREAM_MIDIIN: 483 ossfile.f_mode = FMODE_READ; 484 break; 485 } 486 487 if(!oss_devices[ossid].write || !oss_devices[ossid].read || !oss_devices[ossid].ioctl) { 488 return 0; 489 } 490 //check how much room is left in the circular dma buffer 491 #ifdef KEE 492 tmpbuf = (char NEAR *)&info; 493 #else 494 tmpbuf = (char NEAR *)__StackToFlat((ULONG)&info); 495 #endif 496 if(oss_devices[ossid].ioctl(&ossinode, &ossfile, cmd, (ULONG)tmpbuf)) { 497 return 0; 498 } 499 return info.bytes; 500 } 501 //****************************************************************************** 502 //****************************************************************************** 453 503 ULONG OSS32_SetVolume(ULONG streamtype, ULONG streamid, ULONG cmd, ULONG volume) 454 504 { -
sbliveos2/trunk/readme.txt
r151 r152 1 SoundBlaster Live! OS/2 Audio driver version 0. 35(beta)1 SoundBlaster Live! OS/2 Audio driver version 0.50 (beta) 2 2 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 3 3 4 4 Contents 5 5 ======== 6 1 Description 7 2 Features 8 3 Requirements 9 4 Installation/uninstall 10 5 Config.sys options 11 6 Known problems 12 7 File listing 13 8 Source code 14 9 Contacting the author 15 9.1 SoundBlaster Live OS/2 mailinglist 16 10 Warranty 6 1 Description 7 2 Features 8 3 History 9 4 Requirements 10 5 Installation/uninstall 11 6 Config.sys options 12 7 Known problems 13 8 File listing 14 9 Source code 15 10 Contacting the author 16 10.1 SoundBlaster Live OS/2 mailinglist 17 11 Warranty 17 18 18 19 … … 38 39 39 40 40 3 Requirements 41 3 History 42 ========= 43 0.50 44 - Added RTMIDI playback & recording (MPU401) 45 - Manually detect SB Live! hardware 46 - Bugfix for TRAP D when starting playback/recording 47 48 0.25 49 - First public beta release 50 51 52 4 Requirements 41 53 ============== 42 54 - OS/2 Warp 4 or Warp Server for e-Business … … 45 57 46 58 47 4Installation/uninstall59 5 Installation/uninstall 48 60 ======================== 49 61 To install the SB Live driver: … … 56 68 as described above, but select zero SB Live cards when asked. 57 69 58 5Config.sys options70 6 Config.sys options 59 71 ==================== 60 72 DEVICE=J:\MMOS2\SBLIVE16.SYS /V /C /M /L … … 71 83 72 84 73 6Known problems85 7 Known problems 74 86 ================ 75 87 - Users have reported that sometimes applications can no longer play audio … … 101 113 - reboot 102 114 103 7File listing115 8 File listing 104 116 ============== 105 117 Installation files: … … 124 136 125 137 126 8Source code138 9 Source code 127 139 ============= 128 140 As this driver is based on the open source SoundBlaster Live Linux driver, … … 135 147 136 148 137 9Contacting the author149 10 Contacting the author 138 150 ======================= 139 151 When you find a bug in the driver, you can contact the author by … … 156 168 157 169 158 9.1 SoundBlaster Live OS/2 mailinglist170 10.1 SoundBlaster Live OS/2 mailinglist 159 171 ====================================== 160 172 A mailinglist to discuss the OS/2 sblive driver has been created at egroups.com. … … 162 174 163 175 164 1 0Warranty176 11 Warranty 165 177 =========== 166 178 EXCEPT AS OTHERWISE RESTRICTED BY LAW, THIS WORK IS PROVIDED -
sbliveos2/trunk/sblive/cardwo.c
r142 r152 518 518 /* Save the stop position */ 519 519 emu10k1_voice_getcontrol(wave_out->voice, CCCA_CURRADDR, &wavexferbuf->stopposition); 520 521 520 wavexferbuf->stopposition -= wave_out->voice->params.start; 521 #ifdef TARGET_OS2 522 wavexferbuf->stopposition &= CCCA_CURRADDR_MASK; //SvL: mask off CCCA_8BITSELECT 523 #endif 522 524 523 525 /* Refer to voicemgr.c, CA is not started at zero. We need to take this into account. */ … … 721 723 722 724 emu10k1_voice_getcontrol(wave_out->voice, CCCA_CURRADDR, &curpos); 723 724 725 curpos -= wave_out->voice->params.start; 726 #ifdef TARGET_OS2 727 curpos &= CCCA_CURRADDR_MASK; //SvL: mask off CCCA_8BITSELECT 728 #endif 725 729 726 730 /* Get number of bytes in play buffer per channel.
Note:
See TracChangeset
for help on using the changeset viewer.