davidgiven / cpm65 Goto Github PK
View Code? Open in Web Editor NEWCP/M for the 6502
License: BSD 2-Clause "Simplified" License
CP/M for the 6502
License: BSD 2-Clause "Simplified" License
(I'm interested in CP/M-65 but haven't actually used it, so apologies if this suggestion is inappropriate.)
The CCP knows where a transient command's relocation data starts, and that is the same place its bss starts. COMHDR_TPA_USAGE effectively tells it how large the bss is. It could therefore zero out this region after relocation and before executing the program, which would allow programs to assume their bss is zero-initialised on startup. As I understand it, currently a program has to zero out its own bss if it cares about it actually being zero-initialised.
I appreciate even if the above is reasonable that this is an API change, and programs which relied on this would not work correctly on older versions of CP/M-65.
If you try and extract a substring using A$(4,5) it appears that it is treated as a numeric and the left Bracket is used as exponentiation.
ie if B$="AB" and you print B$(1) you get 5.20200002e+53
Edit: This is on nano6502. This is for printing. If you let A$=B$(1) then print A$ you get the correct result.
Getting this error during the build process:
MKCBMFS src/arch/commodore+c64_cbmfs
munmap_chunk(): invalid pointer
make: *** [.obj/build.mk:1216: .obj/src/arch/commodore+c64_cbmfs/src/arch/commodore+c64_cbmfs.img] Error 1
make: *** [.obj/build.mk:1475: .obj/src/arch/commodore+pet4032_cbmfs/src/arch/commodore+pet4032_cbmfs.img] Error 1
make: *** [.obj/build.mk:1543: .obj/src/arch/commodore+pet8032_cbmfs/src/arch/commodore+pet8032_cbmfs.img] Error 1
make: *** [.obj/build.mk:1611: .obj/src/arch/commodore+pet8096_cbmfs/src/arch/commodore+pet8096_cbmfs.img] Error 1
make: *** [.obj/build.mk:1714: .obj/src/arch/commodore+vic20_cbmfs/src/arch/commodore+vic20_cbmfs.img] Error 1
Thank you for any help you can provide.
Hi,
I have been working on the Atari XL port, and it's working now. BIOS, BDOS and CCP are "under" the ROM so to speak, and there's over 49kB of TPA RAM for user programs now.
But there's a problem with loading fonts. I used to load them at the end of the TPA, lower the upper limit and be done with it. That won't work if that part of RAM is under the ROM. Used to be $bc00, now it'll be $cc00. But everytime the ROM is switched on for CIO or SIO calls the font will disappear and garbage is displayed.
I have already moved the screen memory to low RAM, but that's setup before the rest is run. Now if I want SETFNT.COM to load a font, 1 kilobyte aligned (requirement of the hardware), in low memory, it will overwrite itself while it's running. Is there a way to avoid that? I could reload myself (i.e. setfnt.com loads setfnt.com somewhere higher, relocate it and run again), but I was hoping for something more elegant :)
Regards,
Ivo
Hi,
I stumbled upon some strange behaviour which I cannot explain.
See this code in my test branch: https://github.com/ivop/cpm65/blob/test/apps/test.c
Output:
CP/M-65 for the Atari 800
A>free
ZP: 95 to FF. Free: 6A
TPA: 1900 to BC00. Free: A300
CCP at: B200
A>test
start: 9500
cpm▁ram: 3790
end: ff00
A>
$ xxd -s 1 -l 1 .obj/test.com
00000001: 21 !
Any idea what's going on?
I was compiling a pretty large program and cpm_ram ended up somewhere in the middle of my code.
I have confirmed this with the BBC and Apple][ port.
Regards,
Ivo
Edit: this large program: https://github.com/ivop/cpm65/blob/ubasic/apps/ubasic.c
With a hardcoded higher address it runs. Not that it's a particularly useful basic. It completely lacks syntax errors and is pretty useless, and I won't pursue that path any further, but I was flabbergasted by the weird values of tpa_start and cpm_ram.
I also briefly looked into Palo Alto's Tiny Basic, but that one has no tokenizer.
Microsoft Basic's disassembled source is available in various versions, but that looks daunting to port (CA65 assembly with lots of defines and macro's). Perhaps Altirra Basic. That one is a reimplementation of Atari Basic, open source, 8kB binary, and actively maintained. Rip out the Atari specific stuff like graphics commands, and it could be a viable candidate. Or the Microsoft Basic compatible from "the other CPM-65".
When I try
ren ls.com l.com
the drive works for quite some time, but does not seem to change anything. dir
still shows "ls.com".
BTW: Is there a way to move a file from one "user partition" to anonther?
For my builds I'm using the official llvm-mos-sdk releases.
While everything works just fine with SDK v.4.0.0:
The newer versions v.6.0.0 and v.6.1.0 produce not working result:
I'm focused on Oric but tested Apple][ and it hangs too.
Maybe this is not an issue of cpm65 but IMHO it should be considered when building.
I don't see any implementation of Basic or Pascal. Well, Pascal will be harder to find or write a new one.
But 6502 Basic is already here. Can you compile it with your ASM.COM? I tried, but the format is too different.
BASIC.zip
The order of assembly arguments in the Makefile doesn't work on macOS. bin/cpmemu $(OBJDIR)/asm.com -pA=$(dir $<) -pB=$(dir $@) ...
results in the creation of files named -pA=dir
and .pB=dir
. It works if you move the -p
options in front of the name of the file to execute: bin/cpmemu -pA=$(dir $<) -pB=$(dir $@) $(OBJDIR)/asm.com ...
Hi,
Th last few days I have been working on this. Eventually I plan to run it on top of CP/M-65 to provide BIOS and possibly BDOS functionality. That would need some copying between 8080 and 6502 memory, for example for writestring if the string crosses a memory bank border. What do you think? I'm pretty excited about it. Something I'd wanted to do for years, emulating the 8080 on a 6502.
https://github.com/ivop/atari8080
Not sure how difficult it would be to port it to other systems, too. C64 REU is slow, but apparently there's also a 256kB banked memory upgrade. Master 128 sideways RAM has some limitations I believe, so that might not work or have less than 64kB for the 8080 environment. Apple 2e seems to have 48kB banks, so that'll probably work. You can even do the same BIT trick (see instruction fetcher in 8080.s) if you only use the lower 32kB of the bank and use two banks.
Regards,
Ivo
Edit: I added a pre-assembled binary. Run with atari800 -xe 8080.xex.
I stumbled upon this error with a new program I'm writing, and can reproduce it with copy:
All other ports seem to work correctly when all of bss is used and the program calls cpm_warmboot() after that.
Edit: looks like this has nothing to do with bss being fully used, but running out of disk space. I suppose this shouldn't happen though?
Hi,
The Atari 800XL and later had 64kB of RAM instead of the 48kB maximum of the 400/800 series, but 16kB was "under" the ROM. The ROM can be switched off to access it, but there is a hole were the hardware registers live.
Current CP/M memory map:
0x0000 - 0x04ff zp, stack, Atari OS variables
0x0500 - 0xbbff BIOS, BDOS, TPA, CCP
0xbc00 - 0xbfff DL, screen memory
0xc000 - 0xcfff ROM
0xd000 - 0xd7ff hardware registers
0xd800 - 0xffff ROM
For 800XL and beyond I'd like to lay it out like this:
0x0000 - 0x04ff same
0x0500 - 0xcfff BIOS, DL+screenmem, TPA (screen memory cannot be under ROM, 4kB extra TPA)
0xd000 - 0xd7ff hardware
0xd800 - 0xffff character set copy, minimal keyboard IRQ, BDOS, CCP
What would be an acceptable way to accomplish this? BDOS does not need to claim TPA and warmboot never needs to reload CCP. ROM and interrupts (except for keyboard IRQ) are disabled, except when BIOS needs to call into the Atari OS. Loading sectors into 0xc000-0xcfff directly needs a temporary 128-byte buffer in the BIOS area.
After that, I'd also like to implement a 130XE (or expanded XL) version with an 80-column screen in an extended memory bank that's only visible to ANTIC (the screen processor). Needs ~8Kb or RAM for 320x192 graphics mode, but won't cost you any TPA space if you use extended memory. CPU/ANTIC access can be told to look at base RAM or extended RAM independently.
Regards,
Ivo
Trying to build this project and running into an issue where a cpm.h header file is needed. I have a few (including from the z88dk library) but want to make sure I'm using the correct one.
Thanks for any help you can provide.
-Ron
Hi,
Glancing over the new Oric port, I noticed that you only draw a cursor during screen_getchar. Is that intended behaviour? If so, I could greatly simplify and reduce the code in the Atari port (and future 80 columns driver).
Regards,
Ivo
It seems the Atari always returns carry set, indicating that there's a disk error. Most other programs don't check for this, but BEDIT does. This makes the mametest script fail.
Brain dump below:
SOUND driver
============
Note: 0-119, ten octaves from C0-B9
Channel: 0-2
Volume: 0-15
Noise: 0,1, off/on, ch. 2 only (SN limitation we impose on others,too)
Implementation: three channels is the lowest common denominator of SN, AY,
SID, and Pokey. Leaves channel 4 on Pokey free for timer.
For SID, use pulse with 50/50 duty cycle to get a clear tone.
All others play clear tone. Pokey might use distortion C
for low notes? Notes outside the playable range are either
transposed up/down, or ignored. Setting noise switches to
one of the LFSR noise types.
SOUND_VERSION
Enter:
Exit: A = API version
SOUND_BELL
Enter:
Exit:
SOUND_PLAY_NOTE
Enter: A = Note, X = Channel
Exit:
SOUND_SET_VOLUME
Enter: A = Volume, X = Channel
Exit:
SOUND_SET_NOISE
Enter: A = 0,1, X = Channel
Exit:
SOUND_SET_IGNORE_INVALID
Enter: A = 0,1
Exit:
TIMER driver
============
Granularity of time? Hertz might be computational expensive for platforms
that do not specifically have a timer frequency that can be set in hertz.
Perhaps a list of predefined values?
1Hz, 2Hz, 10Hz, 25Hz, 30Hz, 50Hz, 60Hz, 100Hz, 1000Hz, 15.7kHz (scan line)
Or maybe only one fixed frequency of 1 VBlank?
Commodore PET can only do VB on pin CB1 of its PIA?
Implementation: Oric, C64, BBC, VIC-20: CIA or VIA timer
Atari: Pokey timer or Antic (GPU) VBI
PET: PIA vertical retrace
Apple: No timers? Not even VB? Only IIc seems to have VBI
TIMER_VERSION
Enter:
Exit: A = API major version, X = API minor version
TIMER_GET_CPU_SPEED (API 1)
Enter:
Exit: YXA = 24-bit frequency in Hertz
Could be used for busy waiting timing loops if there's no timer
capability. Of limitted use on systems with cycle stealing (RAM
refresh, display memory access)
TIMER_WAIT_VBLANK (API 2)
Enter: X = Number of times (1-255, 0 is a nop)
Exit:
TIMER_GET_FRAME_RATE (API 2)
Enter:
Exit: A = 50 or 60
TIMER_SET_FREQUENCY (API 3)
Enter: XA = Frequency (?)
Exit:
TIMER_SET_CALLBACK (API 3)
Enter: XA = Address
TIMER_START (API 3)
Enter:
Exit:
TIMER_STOP (API 3)
Enter:
Exit:
JOYSTICK
========
JOYSTICK_VERSION
Enter:
Exit: A = API version
JOYSTICK_GET_STATUS
Enter: A = 0, 1 (joystick 0 or 1)
Exit: A = 0-7,8 (UP, UPRIGHT, RIGHT, etc... clockwise, 8 = center)
X = trigger
Thoughts? 😄
Please put disk images in Releases section. Also, because not everyone has these machines, it will be useful to post instructions how to use these images in an emulator and which one is suitable.
How about adding a command like REN
just for changing the user?
Something like CHGU filename.ext 1
would change the user ID of the file in the current user ID to the specified one by changing the byte in the file system entry/entries. This would make the USER
concept much more usable. If you want to have a file permanently on a different user ID, you can copy it first, CHGU
it, and REN
it on the new user ID.
I've taken a look at CCP.SYS and BDOS.SYS, but my ideas for a solution would involve a lot of copy/paste. Maybe you've got a better idea how to solve this?
Also, you might have got a better idea than CHGU
to name the command.
Having 4MB of space in the filesystem on the Sorbus would really benefit from a less useless concept of user IDs.
I have a test case where adding a single byte makes the difference between working or not; looks like something's wrong with branch expansion.
Working:
0275 e4 07 .. cpx 07
ZPRELO 0276
0277 d0 7e .~ bne 02f7
0279 60 ` rts
Not working:
0276 e4 07 .. cpx 07
ZPRELO 0277
0278 f0 03 .. beq 027d
027a 4c f7 01 L.. jmp 01f7
TPARELO 027c
027d 60 ` rts
That 0x1f7 is very wrong...
Hi,
First of all, I'm very pleased to see that VT52 was turned into a loadable driver. Very nice!
The question was asked how to print escape from Altirra BASIC. That can be done with chr$(). For example:
? chr$(27);"H";chr$(27);"J"
To home the cursor and clear screen.
I noticed that when an invalid escape sequence is encountered, it keeps waiting for a valid one. ? chr$(27)
<enter> and nothing gets echoed to the screen anymore (but Altirra's input routine does receive the characters). After typing H
it "escapes" from that state and everything turns to normal again.
@davidgiven How does one exit atbasic? I can't get out of it! :)
Not a bug but more a question about the lack of a binary for the Nano6502. Should I be bugging the creator of this or is there no auto make as part of your scripts? I wanted to try this out before investing in setting up the lvm-mos environment. Thanks
objdump does not build with the updated lib6502 (#93) as the zpr addressing mode is not handled by the disassembler:
apps/objdump.c:191:46: error: use of undeclared identifier 'zpr_cb'
Looks like we're going to want this soon, so I'm going to rough out the design here...
The rationale is that various applications need different functionality that we don't want in the base BIOS because it occupies RAM. Example: a terminal emulator. These aren't cheap, and we shouldn't require users to have it in memory if they're not using it. In addition, the original CP/M was hard to extend, leading to a miserable experience when trying to, e.g., change the serial port baud rate. Let's not make that mistake here. At the same time, let's try and keep things as lightweight as possible. Conversely, we also want to be able to inline drivers into the BIOS (e.g. for a screen driver).
So the basic concept is: the BIOS keeps a singly-linked list of drivers. Drivers can be looked up by ID, returning a management routine. An application which wants to use a driver will look it up, stash the address, and can then call routines on it. An application which wants to install a driver can register a new driver on the top of the list, then adjust the memory bounds before returning so the driver code stays resident.
Each driver has a management routine for doing stuff like device lookup and enumeration, and one or more strategy routines, which care entirely driver-specific.
New BIOS entrypoints:
BIOS_ADD_DRIVER: writes to the top-of-list address, returning the old address.
Intention: to install a driver, you call this, providing the new driver's management routine. The old address gets stored for use by the management routine.
This would also cause the BIOS to recache any strategy routines it may have (see below).
BIOS_FIND_DRIVER: takes a 16-bit driver ID. Looks up a driver and returns its strategy routine address, or errors out if there isn't one. (This just calls DRV_FIND on the top driver below.)
That's all there is.
The management routine takes an opcode in Y and a parameter in XA.
The BIOS code therefore becomes almost trivial, with one pointer of storage (for the current head-of-list). DRV_FIND is a cmp; bne; cpx; beq; jmp (...)
.
A driver example: the screen.
A TTY:
(The BIOS would always supply a minimal TTY, which the BIOS console in/out routines would call. Loading a new TTY device would override the old one, and therefore the BIOS would start sending characters there instead. This new one could have a much more sophisticated terminal emulator state machine, implementing something like VT52, implemented by calling the screen driver; so making it platform independent.)
Hi,
I started a branch in https://github.com/andreasbaumann/cpm65/tree/apple2_plus to tackle older Apple machines
with 40-column displays only (or later some Videx 80-columns card).
I can report that CP/M-65 runs just fine on my IMC-2001 Apple ][ plus clone, many thanks for that. :-)
I also started a screen driver, but run into a 2K BIOS issue, I think in the loader (hence I disabled or didn't implement some
things yet). Tested with CLS
, SCRNTEST
, LIFE
and QE
, they seem to work so far.
Any ideas how to solve that, but the obvious one: to support reading from extends in loader.S
?
What about loading the screen driver in userland after booting (like capsdrv.asm
)? It's assembled with ASM currently, but
this could also be llvm, I suppose.
Another wild idea is to load a SCREEN.SYS
after BIOS.SYS
(again with a 2K limit).
I can of course also try to learn proper 6502 assembly coding and do some byte shaving.. ;-)
I have an issue in vt52term, where I reuse a global static FCB struct for opening files for both sending and receiving with Xmodem. If I only open/close the files, I get strange behavior such as the file position from the previous file being remembered when opening a new file.
I currently have a working fix where I use memset to zero the entire FCB before starting a new file operation. This works fine, but is this the way it is intended to be done? I tried just setting ex to zero, but this did not solve the problem.
I guess this boils down to two questions:
When I ran the STAT program on the Virtual II Emulator (MAC OS), the output from STAT just over prints each line of the output and then places a PROMPT in col 1 instead of 0.
Hi,
You mentioned a generic BDOS loader in my last pull request's comment section. This would indeed be really helpful in case the size of BDOS changes and avoid having to adjust the bootloader and disk image generation, at least for the Atari. Depending on the amount of assumptions we allow, this could be done in a few lines of code.
__USERTPA_START__
Were you planning on doing this yourself? If not, I could take a stab at it.
Regards,
Ivo
Hi,
I started working on tty80/screen80 for the Atari. I want it as a loadable driver, but I'm stumbling upon a few problems.
First I started in "asm.com" assembly, but noticed I could not create the needed jump table. .byte <label-1 and .byte >label-1 did not work correctly. The 13 entries for screen:
0C19: 3F 3F 3F 3F 3F 3F 3F 3F 3F 3F 3F 3F 4B 0C 0C 0C
0C29: 0C 0C 0C 0C 0C 0C 0C 0C 0C 00
The 3 entries for tty:
0C4F: 61 61 6D 0C 0C 00
The third one here is tty_conout, so that crashes heavily ;) See https://github.com/ivop/cpm65/blob/tty80/apps/a8tty80.asm
Adding an extra dummy byte to the table only works if it is equal to the last one before. Adding .byte 0 messes things up and adds both zeroes to the end of the second table.
0C4F: 63 63 63 6F 0C 0C 0C 00
versus
0C4F: 63 63 63 0C 0C 0C 00 00
So, I tried doing it with clang instead. Added a makefile rule to compile .S instead of .c to .o, link to .com was there already, but somehow the resulting cpm65 binary is not relocated correctly. When the code runs, it is as if it was loaded at $0200 instead of higher up ($0c00 on the XL/XE, $1d00 on the 400/800).
0C30: A0 26 LDY #$26
0C32: 20 04 02 JSR $0204
which should be JSR $0C04 (jmp BDOS). See https://github.com/ivop/cpm65/blob/tty80-2/apps/a8tty80.S
I checked the invocations of llvm with the -v flag, and it seems identical to the C version, except for calling cc1as. But there's a relocation flag there, too.
Anyway, I'm stuck. I could do it all in mads, which I know pretty well, but I'd prefer to use the cpm65 tools.
Regards,
Ivo
I had to merge it in a half-ported state for reasons --- it doesn't work.
I accidently hit ctrl+c while on user area 1 on the nano6502, and this crashed CP/M-65 with the error message "Couldn't open CCP". I could recreate the same thing on a BBC Micro (with b-em) to verify that it is not platform specific.
I haven't investigated further, but I assume it tries to load CCP from the current user area and fails.
A> bedit missing
Loading MISSING
Failed to load file
> list
...garbage...
It's clearly failing to terminate the document on error.
Hi,
I'm observing strange behaviour with CCP. It seems there are leftover bytes from previous commands.
A>bug z*
Z???????
A>dir .txt
A: ASM TXT : BEDIT TXT
A>bug z
Z???????TXT
A>bug zzzzz*
ZZZZZ???K??
Here's the code of bug:
.bss pblock, 165
cpm_fcb = pblock
BDOS_CONOUT = 2
BDOS = start - 3
.zp savex,1
start:
ldx #0
.zrepeat
lda cpm_fcb+1,x
ldy #BDOS_CONOUT
stx savex
jsr BDOS
ldx savex
inx
cpx #11
.zuntil eq
lda #13
ldy #BDOS_CONOUT
jsr BDOS
lda #10
ldy #BDOS_CONOUT
jmp BDOS
Looking over ccp.S it looks like the fcb is properly cleared, but somewhere weird values are written to it. I specifically wrote bug with asm as to rule out the llvm toolchain. Any ideas?
Hi,
Yesterday I figured out how to get the VIC-20 and PET versions working on emulation with Vice (I already had b-em and linapple running, and of course atari800 and Altirra with wine), but I'm having problems with the C64. I got my ROMs here:
http://www.zimmers.net/anonftp/pub/cbm/firmware/computers/
I tried about every combination of kernal and 1541 ROMs I could think of, but none work. It seems my true drive emulation of the 1541 works correctly because it works with xvic. Any idea what might be wrong?
Yesterday it got stuck after printing one dot. Today it doesn't even print a dot and goes back to the ready prompt.
kernal.901227-03.bin
characters.901225-01.bin
basic.901226-01.bin
1541-c000.325302-01.bin
1541-e000.901229-05.bin
True drive emulation enabled
RPM: 300
Wobble: 3000
Amplitude: 2000
I tried disabling wobble completely, but that makes no difference.
Regards,
Ivo
See the discussion on #138.
You mentioned previously somewhere (in a pull request comment I think) that you intended to define constants for the arrow keys. What do you think about using the ASCII-codes for the ctrl-combinations that *nix has used since forever?
Up ctrl+p [0x10]
Down ctrl+n [0x0E]
Left ctrl+b [0x02]
Right ctrl+f [0x06]
Would the best place to add these on the application side be to drivers.inc/screen.h or do you see any use for the arrow keys in tty applications?
I will send you one
You can message me here
https://agonlight.au
When attempting to list multiple lines in BEdit, it prints the first line and then emits 0xFF and stops. Also while listing a single line, it emits 0xFF at the end of the line.
I have tested this on my own computer build, BBC Micro (emulated) and C64 (emulated) and the behavior is the same on all three platforms.
As I see nothing obviously wrong in the assembly code for BEdit I have also tried building it with two different version of llvm-mos (SDK v8.0.1 and SDK v1.0.0 from June) to see if it could be an issue with llvs-mos breaking the assembler, but I get the same result in both cases.
Hi,
Could you also add atari800xlhd.atr to the released disk images? It is currently missing. It's the XL/XE specific port I did recently.
Working on porting the 8080 emulator to CP/M-65, but the warm weather is slowing me down. Also had to do a few tunes for an upcoming game, and did some tests for an 80 columns mode utilizing 3x6 characters and a modified display list to reduce its memory footprint from 7680 to 5760 bytes (excluding the font and driver itself). More on that later.
Regards,
Ivo
Hi,
I tried adding a second drive to the Atari port (https://github.com/ivop/cpm65/tree/atari-multi-drive) but it does not seem to work as expected:
Directory of A: works normally. I switch to B: and then the directory listing is not correct. CTRL-C and the DIR, still wrong. But DIR B: suddenly shows the correct directory. Any idea what's going on here?
Regards,
Ivo
Edit: I looked at the nano6502 port and as far as I can see I did exactly what they did, i.e. return a separate dph for each drive.
How about some DOS/65 compability?
http://www.z80.eu/dos65.html
In the Apple 2e CPM-65, it is not working in Apple 22JSE (https://www.scullinsteel.com/apple/e) because the bank switch is not working.
The source code is using two STA $C08b to switch the bank. Based on "Understanding the Apple IIe, by James Fielding Sather, Pg 5-24", bank switch should only work with reading twice on $C08b. Manually changing editing the image with two LDA $C08B fixes the issue.
Excerpt from apple2e.S
sta MEM_BANKING+0xb ; R/W 0xe000 RAM; bank 1 in 0xd000 <- should be changed to lda MEM_BANKING+0xb
sta MEM_BANKING+0xb ; yes, I'm sure <- should be changed to lda MEM_BANKING+0xb
How about supporting o65?
With the Apple II GS and the Apple II C plus, there are two machines capable of running CP/M 65, but can't due to a different disk format (3.5" instead of 5.25").
(And I'd really like to see CP/M 65 running on my real 2gs instead of just in an emulator.)
I have been experimenting with a BIOS for my homebrew computer with two drives (which in this case are two different cmpfs-files on a USB-drive), and it kind of works but I get som strange results:
Is support for multiple drives fully implemented or am I trying to do something which is not supposed to work at this point?
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.